#[cfg(any(unix, windows))]
use std::path::Path;
#[cfg(unix)]
use std::process::{Command, ExitStatus, Stdio};
use std::{
borrow::Cow,
fmt,
future::Future,
io,
net::SocketAddr,
pin::Pin,
task::{Context, Poll},
time::Duration,
};
#[cfg(unix)]
use super::io::unix_socket_address;
use super::{Interest, IoSource, traits};
#[derive(Clone, Debug)]
pub(crate) struct ZRuntime(zruntime::SharedRuntime);
impl ZRuntime {
pub(crate) fn new() -> io::Result<Self> {
Ok(Self(zruntime::SharedRuntime::current()?))
}
pub(super) fn spawn_named<T>(
&self,
name: Cow<'static, str>,
future: impl Future<Output = T> + Send + 'static,
) -> Task<T>
where
T: Send + 'static,
{
Task(self.0.spawn(name, future))
}
}
impl traits::Runtime for ZRuntime {
type RegisteredIoSource = Registration;
type Sleep = zruntime::Sleep<zruntime::Shared>;
type Task<T>
= Task<T>
where
T: Send + 'static;
fn register_io_source(&self, source: IoSource) -> io::Result<Registration> {
self.0.register(source).map(Registration)
}
fn sleep(&self, duration: Duration) -> zruntime::Sleep<zruntime::Shared> {
self.0.sleep(duration)
}
fn spawn<T>(&self, name: &str, future: impl Future<Output = T> + Send + 'static) -> Task<T>
where
T: Send + 'static,
{
self.spawn_named(name.to_owned().into(), future)
}
async fn connect_tcp(&self, address: SocketAddr) -> io::Result<IoSource> {
let stream = zruntime::net::TcpStream::<zruntime::Shared>::connect(&self.0, address)
.await?
.into_std();
Ok(IoSource::new(stream.into()))
}
#[cfg(unix)]
fn connect_unix(&self, path: &Path) -> impl Future<Output = io::Result<IoSource>> + Send {
let address = unix_socket_address(path);
async move {
let stream = zruntime::net::unix::UnixStream::<zruntime::Shared>::connect_addr(
&self.0, &address?,
)
.await?
.into_std();
Ok(IoSource::new(stream.into()))
}
}
#[cfg(windows)]
async fn connect_unix(&self, path: &Path) -> io::Result<IoSource> {
use socket2::{Domain, SockAddr};
let address = SockAddr::unix(path)?;
traits::Runtime::spawn_blocking(self, move || {
super::io::connect_blocking(Domain::UNIX, &address)
})
.await
}
#[cfg(unix)]
fn spawn_process(
&self,
command: Command,
stdin: Stdio,
stdout: Stdio,
stderr: Stdio,
) -> io::Result<Pin<Box<dyn Future<Output = io::Result<ExitStatus>> + Send + 'static>>> {
let mut command = zruntime::process::Command::from(command);
command.stdin(stdin).stdout(stdout).stderr(stderr);
let child = command.spawn(&self.0);
drop(command);
let mut child = child?;
Ok(Box::pin(async move { child.status().await }))
}
}
#[cfg(not(feature = "tokio"))]
pub(crate) fn block_on<F>(future: F) -> F::Output
where
F: Future,
{
zruntime::block_on(future)
}
#[derive(Debug)]
pub(crate) struct Registration(zruntime::Registration<zruntime::Shared>);
impl traits::PollIo for Registration {
fn poll_io<T>(
&self,
cx: &mut Context<'_>,
interest: Interest,
operation: impl FnMut() -> io::Result<T>,
) -> Poll<io::Result<T>> {
self.0.poll_io(cx, zruntime_interest(interest), operation)
}
}
fn zruntime_interest(interest: Interest) -> zruntime::Interest {
match interest {
Interest::Readable => zruntime::Interest::Readable,
Interest::Writable => zruntime::Interest::Writable,
}
}
pub(crate) struct Task<T>(zruntime::Task<T, zruntime::Shared>);
impl<T> fmt::Debug for Task<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Task").field(&self.0).finish()
}
}
impl<T> Future for Task<T> {
type Output = io::Result<T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.get_mut().0).poll(cx)
}
}
impl<T> traits::TaskHandle<T> for Task<T>
where
T: Send + 'static,
{
fn detach(self) {
self.0.detach();
}
}
#[cfg(test)]
mod tests {
use ntest::timeout;
use super::*;
use crate::runtime::traits::Runtime as _;
#[test]
#[timeout(15000)]
fn a_spawned_task_hands_its_output_back() {
let runtime = ZRuntime::new().expect("a runtime for this thread");
let task = runtime.spawn("an answer", async { 42 });
assert_eq!(zruntime::block_on(task).unwrap(), 42);
}
}