use super::{Context, Future, Pin, Poll};
static TOKIO_RUNTIME: std::sync::OnceLock<std::sync::Mutex<Option<tokio::runtime::Runtime>>> =
std::sync::OnceLock::new();
static TOKIO_HANDLE: std::sync::OnceLock<tokio::runtime::Handle> = std::sync::OnceLock::new();
fn runtime_handle() -> tokio::runtime::Handle {
TOKIO_HANDLE.get().cloned().expect(
"dtact-io tokio runtime not initialised — \
call dtact_io::init_runtime() before performing any I/O",
)
}
pub fn init_runtime(
workers: usize,
_ring_depth: u32,
_buffer_pool_size: usize,
_chunk_size: usize,
_pin_cpus: &[usize],
) {
TOKIO_RUNTIME.get_or_init(|| {
let rt = tokio::runtime::Builder::new_multi_thread()
.worker_threads(workers.max(1))
.enable_all()
.build()
.expect("Failed to build Tokio runtime");
let _ = TOKIO_HANDLE.set(rt.handle().clone());
std::sync::Mutex::new(Some(rt))
});
}
pub fn init(workers: usize) {
init_runtime(workers, 0, 0, 0, &[]);
}
pub fn shutdown_runtime() {
if let Some(cell) = TOKIO_RUNTIME.get()
&& let Ok(mut guard) = cell.lock()
&& let Some(rt) = guard.take()
{
rt.shutdown_background();
}
}
#[must_use]
pub fn get_runtime_handle() -> tokio::runtime::Handle {
runtime_handle()
}
#[doc(hidden)]
pub struct TokioFutureWrapper<F> {
inner: F,
}
impl<F: Future> Future for TokioFutureWrapper<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let _guard = runtime_handle().enter();
let inner = unsafe { self.map_unchecked_mut(|s| &mut s.inner) };
inner.poll(cx)
}
}
#[cfg(unix)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OpCode {
Read,
Write,
Accept,
Connect,
}
#[cfg(unix)]
pub struct DtactIoFuture {
pub worker_idx: usize,
pub fd: u32,
pub direct_fd_idx: u32,
pub op: OpCode,
pub buf_ptr: *mut u8,
pub len: usize,
pub offset: i64,
pub addr: Option<libc::sockaddr_storage>,
pub addr_len: libc::socklen_t,
pub slot_idx: Option<usize>,
async_fd: Option<tokio::io::unix::AsyncFd<std::os::unix::io::RawFd>>,
}
#[cfg(unix)]
unsafe impl Send for DtactIoFuture {}
#[cfg(unix)]
unsafe impl Sync for DtactIoFuture {}
#[cfg(unix)]
impl DtactIoFuture {
#[allow(clippy::too_many_arguments)]
pub const fn new(
worker_idx: usize,
fd: u32,
direct_fd_idx: u32,
op: OpCode,
buf_ptr: *mut u8,
len: usize,
offset: i64,
addr: Option<libc::sockaddr_storage>,
addr_len: libc::socklen_t,
slot_idx: Option<usize>,
) -> Self {
Self {
worker_idx,
fd,
direct_fd_idx,
op,
buf_ptr,
len,
offset,
addr,
addr_len,
slot_idx,
async_fd: None,
}
}
#[inline]
fn try_syscall(
fd: std::os::unix::io::RawFd,
op: OpCode,
buf_ptr: *mut u8,
len: usize,
addr: *const libc::sockaddr_storage,
addr_len: libc::socklen_t,
) -> std::io::Result<usize> {
let r = match op {
OpCode::Read => unsafe { libc::read(fd, buf_ptr.cast::<libc::c_void>(), len) },
OpCode::Write => unsafe { libc::write(fd, buf_ptr.cast::<libc::c_void>(), len) },
OpCode::Accept => unsafe {
libc::accept(fd, std::ptr::null_mut(), std::ptr::null_mut()) as isize
},
OpCode::Connect => {
let mut err: libc::c_int = 0;
let mut err_len = std::mem::size_of::<libc::c_int>() as libc::socklen_t;
let r = unsafe {
libc::getsockopt(
fd,
libc::SOL_SOCKET,
libc::SO_ERROR,
(&raw mut err).cast::<libc::c_void>(),
&raw mut err_len,
)
};
if r == 0 && err != 0 {
return Err(std::io::Error::from_raw_os_error(err));
}
let r = unsafe { libc::connect(fd, addr.cast::<libc::sockaddr>(), addr_len) };
if r < 0 {
let e = std::io::Error::last_os_error();
let os_err = e.raw_os_error();
if os_err == Some(libc::EISCONN) {
return Ok(0);
}
return Err(e);
}
return Ok(0);
}
};
if r < 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(r as usize)
}
}
#[inline]
fn is_blocking_error(e: &std::io::Error) -> bool {
let kind = e.kind();
kind == std::io::ErrorKind::WouldBlock
|| e.raw_os_error() == Some(libc::EINPROGRESS)
|| e.raw_os_error() == Some(libc::EALREADY)
|| e.raw_os_error() == Some(libc::EINTR)
}
}
#[cfg(unix)]
impl Future for DtactIoFuture {
type Output = std::io::Result<usize>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = unsafe { self.get_unchecked_mut() };
let fd = this.fd as std::os::unix::io::RawFd;
let op = this.op;
let buf_ptr = this.buf_ptr;
let len = this.len;
let addr_ptr: *const libc::sockaddr_storage = this
.addr
.as_ref()
.map_or(std::ptr::null(), std::ptr::from_ref);
let addr_len = this.addr_len;
if this.async_fd.is_none() {
match Self::try_syscall(fd, op, buf_ptr, len, addr_ptr, addr_len) {
Ok(n) => return Poll::Ready(Ok(n)),
Err(ref e) if Self::is_blocking_error(e) => {
let afd = {
let _guard = runtime_handle().enter();
tokio::io::unix::AsyncFd::new(fd)
};
match afd {
Ok(afd) => this.async_fd = Some(afd),
Err(e) => return Poll::Ready(Err(e)),
}
}
Err(e) => return Poll::Ready(Err(e)),
}
}
let is_read_op = matches!(op, OpCode::Read | OpCode::Accept);
let afd = this.async_fd.as_ref().unwrap();
let mut guard = if is_read_op {
match afd.poll_read_ready(cx) {
Poll::Ready(Ok(g)) => g,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Pending => return Poll::Pending,
}
} else {
match afd.poll_write_ready(cx) {
Poll::Ready(Ok(g)) => g,
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Pending => return Poll::Pending,
}
};
match Self::try_syscall(fd, op, buf_ptr, len, addr_ptr, addr_len) {
Ok(n) => Poll::Ready(Ok(n)),
Err(ref e) if Self::is_blocking_error(e) => {
guard.clear_ready();
Poll::Pending
}
Err(e) => Poll::Ready(Err(e)),
}
}
}
#[cfg(unix)]
impl Drop for DtactIoFuture {
fn drop(&mut self) {
drop(self.async_fd.take());
}
}
pub struct DtactTcpStream {
inner: tokio::net::TcpStream,
}
impl DtactTcpStream {
pub fn from_std(stream: std::net::TcpStream) -> std::io::Result<Self> {
stream.set_nonblocking(true)?;
stream.set_nodelay(true)?;
let _guard = runtime_handle().enter();
let inner = tokio::net::TcpStream::from_std(stream)?;
Ok(Self { inner })
}
pub async fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
loop {
match self.inner.try_read(buf) {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.readable();
TokioFutureWrapper { inner: fut }.await?;
}
}
pub async fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
loop {
match self.inner.try_write(buf) {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.writable();
TokioFutureWrapper { inner: fut }.await?;
}
}
pub async fn connect(addr: std::net::SocketAddr) -> std::io::Result<Self> {
let handle = runtime_handle();
let fut = {
let _guard = handle.enter();
tokio::net::TcpStream::connect(addr)
};
let inner = TokioFutureWrapper { inner: fut }.await?;
inner.set_nodelay(true)?;
Ok(Self { inner })
}
}
impl crate::io::AsyncRead for DtactTcpStream {
async fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
self.read(buf).await
}
}
impl crate::io::AsyncWrite for DtactTcpStream {
async fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
self.write(buf).await
}
}
pub struct DtactTcpListener {
inner: tokio::net::TcpListener,
}
impl DtactTcpListener {
pub fn from_std(listener: std::net::TcpListener) -> std::io::Result<Self> {
listener.set_nonblocking(true)?;
let _guard = runtime_handle().enter();
let inner = tokio::net::TcpListener::from_std(listener)?;
Ok(Self { inner })
}
pub async fn accept(&self) -> std::io::Result<(DtactTcpStream, std::net::SocketAddr)> {
let fut = {
let _guard = runtime_handle().enter();
self.inner.accept()
};
let (stream, addr) = TokioFutureWrapper { inner: fut }.await?;
stream.set_nodelay(true)?;
Ok((DtactTcpStream { inner: stream }, addr))
}
}
#[cfg(unix)]
pub struct DtactUnixStream {
inner: tokio::net::UnixStream,
}
#[cfg(unix)]
impl DtactUnixStream {
pub fn from_std(stream: std::os::unix::net::UnixStream) -> std::io::Result<Self> {
stream.set_nonblocking(true)?;
let _guard = runtime_handle().enter();
let inner = tokio::net::UnixStream::from_std(stream)?;
Ok(Self { inner })
}
pub async fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
loop {
match self.inner.try_read(buf) {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.readable();
TokioFutureWrapper { inner: fut }.await?;
}
}
pub async fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
loop {
match self.inner.try_write(buf) {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.writable();
TokioFutureWrapper { inner: fut }.await?;
}
}
pub async fn connect(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
let handle = runtime_handle();
let fut = {
let _guard = handle.enter();
tokio::net::UnixStream::connect(path)
};
let inner = TokioFutureWrapper { inner: fut }.await?;
Ok(Self { inner })
}
pub fn peer_cred(&self) -> std::io::Result<tokio::net::unix::UCred> {
self.inner.peer_cred()
}
}
#[cfg(unix)]
impl crate::io::AsyncRead for DtactUnixStream {
async fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
self.read(buf).await
}
}
#[cfg(unix)]
impl crate::io::AsyncWrite for DtactUnixStream {
async fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
self.write(buf).await
}
}
#[cfg(unix)]
pub struct DtactUnixListener {
inner: tokio::net::UnixListener,
}
#[cfg(unix)]
impl DtactUnixListener {
pub fn bind(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
let _guard = runtime_handle().enter();
let inner = tokio::net::UnixListener::bind(path)?;
Ok(Self { inner })
}
pub fn from_std(listener: std::os::unix::net::UnixListener) -> std::io::Result<Self> {
listener.set_nonblocking(true)?;
let _guard = runtime_handle().enter();
let inner = tokio::net::UnixListener::from_std(listener)?;
Ok(Self { inner })
}
pub async fn accept(&self) -> std::io::Result<(DtactUnixStream, tokio::net::unix::SocketAddr)> {
let fut = {
let _guard = runtime_handle().enter();
self.inner.accept()
};
let (stream, addr) = TokioFutureWrapper { inner: fut }.await?;
Ok((DtactUnixStream { inner: stream }, addr))
}
}
pub struct DtactUdpSocket {
inner: tokio::net::UdpSocket,
}
impl DtactUdpSocket {
pub fn bind(addr: std::net::SocketAddr) -> impl Future<Output = std::io::Result<Self>> {
std::future::ready(std::net::UdpSocket::bind(addr).and_then(Self::from_std))
}
pub fn from_std(socket: std::net::UdpSocket) -> std::io::Result<Self> {
socket.set_nonblocking(true)?;
let _guard = runtime_handle().enter();
let inner = tokio::net::UdpSocket::from_std(socket)?;
Ok(Self { inner })
}
pub fn local_addr(&self) -> std::io::Result<std::net::SocketAddr> {
self.inner.local_addr()
}
pub async fn send_to(
&self,
buf: &[u8],
target: std::net::SocketAddr,
) -> std::io::Result<usize> {
loop {
match self.inner.try_send_to(buf, target) {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.writable();
TokioFutureWrapper { inner: fut }.await?;
}
}
pub async fn recv_from(
&self,
buf: &mut [u8],
) -> std::io::Result<(usize, std::net::SocketAddr)> {
loop {
match self.inner.try_recv_from(buf) {
Ok(pair) => return Ok(pair),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.readable();
TokioFutureWrapper { inner: fut }.await?;
}
}
pub async fn connect(&self, addr: std::net::SocketAddr) -> std::io::Result<()> {
let fut = {
let _guard = runtime_handle().enter();
self.inner.connect(addr)
};
TokioFutureWrapper { inner: fut }.await
}
pub async fn send(&self, buf: &[u8]) -> std::io::Result<usize> {
loop {
match self.inner.try_send(buf) {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.writable();
TokioFutureWrapper { inner: fut }.await?;
}
}
pub async fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
loop {
match self.inner.try_recv(buf) {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.readable();
TokioFutureWrapper { inner: fut }.await?;
}
}
}
#[cfg(unix)]
pub struct DtactUnixDatagram {
inner: tokio::net::UnixDatagram,
}
#[cfg(unix)]
impl DtactUnixDatagram {
pub fn bind(path: impl AsRef<std::path::Path>) -> std::io::Result<Self> {
let _guard = runtime_handle().enter();
let inner = tokio::net::UnixDatagram::bind(path)?;
Ok(Self { inner })
}
pub fn unbound() -> std::io::Result<Self> {
let _guard = runtime_handle().enter();
let inner = tokio::net::UnixDatagram::unbound()?;
Ok(Self { inner })
}
pub async fn send_to(
&self,
buf: &[u8],
target: impl AsRef<std::path::Path>,
) -> std::io::Result<usize> {
loop {
match self.inner.try_send_to(buf, target.as_ref()) {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.writable();
TokioFutureWrapper { inner: fut }.await?;
}
}
pub async fn recv_from(
&self,
buf: &mut [u8],
) -> std::io::Result<(usize, tokio::net::unix::SocketAddr)> {
loop {
match self.inner.try_recv_from(buf) {
Ok(pair) => return Ok(pair),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.readable();
TokioFutureWrapper { inner: fut }.await?;
}
}
pub async fn connect(&self, target: impl AsRef<std::path::Path>) -> std::io::Result<()> {
self.inner.connect(target)
}
pub async fn send(&self, buf: &[u8]) -> std::io::Result<usize> {
loop {
match self.inner.try_send(buf) {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.writable();
TokioFutureWrapper { inner: fut }.await?;
}
}
pub async fn recv(&self, buf: &mut [u8]) -> std::io::Result<usize> {
loop {
match self.inner.try_recv(buf) {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
let fut = self.inner.readable();
TokioFutureWrapper { inner: fut }.await?;
}
}
}
#[cfg(unix)]
pub struct DtactFifoReader {
inner: tokio::net::unix::pipe::Receiver,
}
#[cfg(unix)]
impl DtactFifoReader {
pub async fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
use tokio::io::AsyncReadExt;
(&self.inner).read(buf).await
}
}
#[cfg(unix)]
pub struct DtactFifoWriter {
inner: tokio::net::unix::pipe::Sender,
}
#[cfg(unix)]
impl DtactFifoWriter {
pub async fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
use tokio::io::AsyncWriteExt;
(&self.inner).write(buf).await
}
}
#[cfg(unix)]
pub fn open_fifo_read(path: impl AsRef<std::path::Path>) -> std::io::Result<DtactFifoReader> {
let _guard = runtime_handle().enter();
let inner = tokio::net::unix::pipe::OpenOptions::new().open_receiver(path)?;
Ok(DtactFifoReader { inner })
}
#[cfg(unix)]
pub fn open_fifo_write(path: impl AsRef<std::path::Path>) -> std::io::Result<DtactFifoWriter> {
let _guard = runtime_handle().enter();
let inner = tokio::net::unix::pipe::OpenOptions::new().open_sender(path)?;
Ok(DtactFifoWriter { inner })
}
pub struct DtactCompat<T>(T);
impl<T> DtactCompat<T> {
pub const fn new(inner: T) -> Self {
Self(inner)
}
pub fn into_inner(self) -> T {
self.0
}
pub const fn get_ref(&self) -> &T {
&self.0
}
pub const fn get_mut(&mut self) -> &mut T {
&mut self.0
}
}
pub trait DtactCompatExt: Sized {
fn compat(self) -> DtactCompat<Self>;
}
impl DtactCompatExt for DtactTcpStream {
fn compat(self) -> DtactCompat<Self> {
DtactCompat(self)
}
}
#[cfg(unix)]
impl DtactCompatExt for DtactIoFuture {
fn compat(self) -> DtactCompat<Self> {
DtactCompat(self)
}
}
impl<F: Future> Future for DtactCompat<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let inner = unsafe { self.map_unchecked_mut(|s| &mut s.0) };
inner.poll(cx)
}
}
impl futures_io::AsyncRead for DtactCompat<DtactTcpStream> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<std::io::Result<usize>> {
let this = self.get_mut();
loop {
match this.0.inner.try_read(buf) {
Ok(n) => return Poll::Ready(Ok(n)), Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Poll::Ready(Err(e)),
}
match this.0.inner.poll_read_ready(cx) {
Poll::Ready(Ok(())) => {} Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Pending => return Poll::Pending,
}
}
}
}
impl futures_io::AsyncWrite for DtactCompat<DtactTcpStream> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
let this = self.get_mut();
loop {
match this.0.inner.try_write(buf) {
Ok(n) => return Poll::Ready(Ok(n)),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Poll::Ready(Err(e)),
}
match this.0.inner.poll_write_ready(cx) {
Poll::Ready(Ok(())) => {}
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Pending => return Poll::Pending,
}
}
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
}
}
impl tokio::io::AsyncRead for DtactCompat<DtactTcpStream> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut tokio::io::ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
let this = self.get_mut();
loop {
let unfilled = buf.initialize_unfilled();
match this.0.inner.try_read(unfilled) {
Ok(0) => return Poll::Ready(Ok(())), Ok(n) => {
buf.advance(n);
return Poll::Ready(Ok(()));
}
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Poll::Ready(Err(e)),
}
match this.0.inner.poll_read_ready(cx) {
Poll::Ready(Ok(())) => {}
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Pending => return Poll::Pending,
}
}
}
}
impl tokio::io::AsyncWrite for DtactCompat<DtactTcpStream> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
let this = self.get_mut();
loop {
match this.0.inner.try_write(buf) {
Ok(n) => return Poll::Ready(Ok(n)),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Poll::Ready(Err(e)),
}
match this.0.inner.poll_write_ready(cx) {
Poll::Ready(Ok(())) => {}
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
Poll::Pending => return Poll::Pending,
}
}
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
Poll::Ready(Ok(()))
}
}
pub async fn lookup_host(
host: impl tokio::net::ToSocketAddrs,
) -> std::io::Result<impl Iterator<Item = std::net::SocketAddr>> {
let fut = {
let _guard = runtime_handle().enter();
tokio::net::lookup_host(host)
};
let resolved = TokioFutureWrapper { inner: fut }.await?;
Ok(resolved.collect::<Vec<_>>().into_iter())
}
#[cfg(windows)]
pub struct DtactNamedPipeHandle {
inner: NamedPipeInner,
}
#[cfg(windows)]
enum NamedPipeInner {
Server(tokio::net::windows::named_pipe::NamedPipeServer),
Client(tokio::net::windows::named_pipe::NamedPipeClient),
}
#[cfg(windows)]
impl DtactNamedPipeHandle {
pub async fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
loop {
let result = match &self.inner {
NamedPipeInner::Server(s) => s.try_read(buf),
NamedPipeInner::Client(c) => c.try_read(buf),
};
match result {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
match &self.inner {
NamedPipeInner::Server(s) => {
TokioFutureWrapper {
inner: s.readable(),
}
.await?
}
NamedPipeInner::Client(c) => {
TokioFutureWrapper {
inner: c.readable(),
}
.await?
}
}
}
}
pub async fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
loop {
let result = match &self.inner {
NamedPipeInner::Server(s) => s.try_write(buf),
NamedPipeInner::Client(c) => c.try_write(buf),
};
match result {
Ok(n) => return Ok(n),
Err(ref e) if e.kind() == std::io::ErrorKind::WouldBlock => {}
Err(e) => return Err(e),
}
match &self.inner {
NamedPipeInner::Server(s) => {
TokioFutureWrapper {
inner: s.writable(),
}
.await?
}
NamedPipeInner::Client(c) => {
TokioFutureWrapper {
inner: c.writable(),
}
.await?
}
}
}
}
}
#[cfg(windows)]
impl crate::io::AsyncRead for DtactNamedPipeHandle {
async fn read(&self, buf: &mut [u8]) -> std::io::Result<usize> {
self.read(buf).await
}
}
#[cfg(windows)]
impl crate::io::AsyncWrite for DtactNamedPipeHandle {
async fn write(&self, buf: &[u8]) -> std::io::Result<usize> {
self.write(buf).await
}
}
#[cfg(windows)]
pub struct DtactNamedPipeServer {
inner: tokio::net::windows::named_pipe::NamedPipeServer,
}
#[cfg(windows)]
impl DtactNamedPipeServer {
pub fn create(name: &str) -> std::io::Result<Self> {
let _guard = runtime_handle().enter();
let inner = tokio::net::windows::named_pipe::ServerOptions::new().create(name)?;
Ok(Self { inner })
}
pub async fn connect(self) -> std::io::Result<DtactNamedPipeHandle> {
let fut = {
let _guard = runtime_handle().enter();
self.inner.connect()
};
TokioFutureWrapper { inner: fut }.await?;
Ok(DtactNamedPipeHandle {
inner: NamedPipeInner::Server(self.inner),
})
}
}
#[cfg(windows)]
pub struct DtactNamedPipeClient;
#[cfg(windows)]
impl DtactNamedPipeClient {
pub async fn connect(name: &str) -> std::io::Result<DtactNamedPipeHandle> {
let (tx, rx) = crate::sync::oneshot::channel();
let name_owned = name.to_string();
let handle_rt = runtime_handle();
std::thread::Builder::new()
.name("dtact-io-namedpipe-connect".into())
.spawn(move || {
let _guard = handle_rt.enter();
let result =
tokio::net::windows::named_pipe::ClientOptions::new().open(&name_owned);
let _ = tx.send(result);
})
.expect("failed to spawn dtact-io named-pipe connect thread");
let inner = rx.await.unwrap_or_else(|_| {
Err(std::io::Error::other(
"dtact-io: named-pipe connect thread panicked before sending a result",
))
})?;
Ok(DtactNamedPipeHandle {
inner: NamedPipeInner::Client(inner),
})
}
}