#[cfg(feature = "once_cell_try")]
use std::sync::OnceLock;
use std::{io, marker::PhantomData};
use compio_buf::IntoInner;
use compio_driver::AsRawFd;
#[cfg(not(feature = "once_cell_try"))]
use once_cell::sync::OnceCell as OnceLock;
use crate::Runtime;
#[derive(Debug, Clone)]
pub struct Attacher {
once: OnceLock<usize>,
_p: PhantomData<*mut ()>,
}
impl Attacher {
pub const fn new() -> Self {
Self {
once: OnceLock::new(),
_p: PhantomData,
}
}
pub fn attach(&self, source: &impl AsRawFd) -> io::Result<()> {
let r = Runtime::current();
let inner = r.inner();
let id = self.once.get_or_try_init(|| {
inner.attach(source.as_raw_fd())?;
io::Result::Ok(inner.id())
})?;
if id != &inner.id() {
Err(io::Error::new(
io::ErrorKind::InvalidInput,
"the current runtime is not the attached runtime",
))
} else {
Ok(())
}
}
pub fn is_attached(&self) -> bool {
self.once.get().is_some()
}
pub fn try_clone(&self, source: &impl AsRawFd) -> io::Result<Self> {
if cfg!(windows) {
Ok(self.clone())
} else {
let new_self = Self::new();
if self.is_attached() {
new_self.attach(source)?;
}
Ok(new_self)
}
}
}
pub trait Attachable {
fn attach(&self) -> io::Result<()>;
fn is_attached(&self) -> bool;
}
pub struct Unattached<T: Attachable>(T);
impl<T: Attachable> Unattached<T> {
pub fn new(a: T) -> Result<Self, T> {
if a.is_attached() { Err(a) } else { Ok(Self(a)) }
}
pub unsafe fn new_unchecked(a: T) -> Self {
Self(a)
}
}
impl<T: Attachable> IntoInner for Unattached<T> {
type Inner = T;
fn into_inner(self) -> Self::Inner {
self.0
}
}
unsafe impl<T: Attachable> Send for Unattached<T> {}
unsafe impl<T: Attachable> Sync for Unattached<T> {}
#[macro_export]
#[doc(hidden)]
macro_rules! impl_attachable {
($t:ty, $inner:ident) => {
impl $crate::Attachable for $t {
fn attach(&self) -> ::std::io::Result<()> {
self.$inner.attach()
}
fn is_attached(&self) -> bool {
self.$inner.is_attached()
}
}
};
}