use std::{
borrow::Borrow,
cell::{RefCell, RefMut},
future::Future,
mem::{self, ManuallyDrop},
ops::{Deref, DerefMut},
pin::Pin,
rc::{self, Rc},
sync::{self, Arc, Mutex, MutexGuard, PoisonError},
thread,
};
use crate::{runtime::Core, scheduler::TaskWaker};
#[cfg(unix)]
use std::os::fd::{AsFd as AsSource, BorrowedFd as BorrowedSource};
#[cfg(windows)]
use std::os::windows::io::{AsSocket as AsSource, BorrowedSocket as BorrowedSource};
pub trait Mode: sealed::Sealed {}
#[derive(Debug)]
pub enum Local {}
impl Mode for Local {}
#[derive(Debug)]
pub enum Shared {}
impl Mode for Shared {}
pub trait Source<M = Local>: AsSource + 'static + sealed::IntoSource<M>
where
M: Mode,
{
}
impl<T, M> Source<M> for T
where
M: Mode,
T: AsSource + 'static + sealed::IntoSource<M>,
{
}
pub(crate) mod sealed {
use super::*;
pub trait Sealed: Sized + 'static {
type Ptr<T>: Clone + Deref<Target = T> + Unpin;
type Weak<T>: Unpin;
type Lock<T>: Lock<T>;
type BoxFuture: Future<Output = ()> + Unpin + 'static;
type HeldWaker: Borrow<TaskWaker> + From<TaskWaker> + 'static;
type SourcePtr: Clone + 'static;
#[cfg(feature = "helper")]
type Seat: Default;
fn new_ptr<T>(value: T) -> Self::Ptr<T>;
fn into_inner<T>(ptr: Self::Ptr<T>) -> T;
fn downgrade<T>(ptr: &Self::Ptr<T>) -> Self::Weak<T>;
fn upgrade<T>(weak: &Self::Weak<T>) -> Option<Self::Ptr<T>>;
fn as_source(source: &Self::SourcePtr) -> BorrowedSource<'_>;
#[cfg(any(
feature = "tcp",
feature = "udp",
all(feature = "unix", unix),
all(feature = "process", unix)
))]
fn source_ptr<T>(ptr: Self::Ptr<T>) -> Self::SourcePtr
where
T: AsSource + Send + Sync + 'static;
fn block_on<F>(core: &Self::Ptr<Core<Self>>, future: F) -> F::Output
where
Self: Mode,
F: Future;
fn ensure_progress(core: &Self::Ptr<Core<Self>>)
where
Self: Mode;
fn set_driven(core: Option<&Self::Ptr<Core<Self>>>)
where
Self: Mode;
fn driven() -> Option<Self::Ptr<Core<Self>>>
where
Self: Mode;
}
pub trait IntoSource<M>: Sized
where
M: Mode,
{
fn source_ptr(ptr: M::Ptr<Self>) -> M::SourcePtr;
}
impl<T> IntoSource<Local> for T
where
T: AsSource + 'static,
{
fn source_ptr(ptr: Rc<T>) -> Rc<dyn AsSource> {
ptr
}
}
impl<T> IntoSource<Shared> for T
where
T: AsSource + Send + Sync + 'static,
{
fn source_ptr(ptr: Arc<T>) -> Arc<dyn AsSource + Send + Sync> {
ptr
}
}
pub trait Lock<T> {
type Guard<'a>: DerefMut<Target = T>
where
Self: 'a;
fn new(value: T) -> Self;
fn lock(&self) -> Self::Guard<'_>;
fn get_mut(&mut self) -> &mut T;
}
impl Sealed for Local {
type Ptr<T> = Rc<T>;
type Weak<T> = rc::Weak<T>;
type Lock<T> = RefCell<T>;
type BoxFuture = Pin<Box<dyn Future<Output = ()>>>;
type HeldWaker = Arc<TaskWaker>;
type SourcePtr = Rc<dyn AsSource>;
#[cfg(feature = "helper")]
type Seat = ();
fn new_ptr<T>(value: T) -> Rc<T> {
Rc::new(value)
}
fn into_inner<T>(ptr: Rc<T>) -> T {
Rc::try_unwrap(ptr).unwrap_or_else(|_| {
unreachable!(
"the reactor keeps a source past its registration only while a wait runs, and \
the wait of a local runtime runs on this thread, never across a call into \
user code"
)
})
}
fn downgrade<T>(ptr: &Rc<T>) -> rc::Weak<T> {
Rc::downgrade(ptr)
}
fn upgrade<T>(weak: &rc::Weak<T>) -> Option<Rc<T>> {
weak.upgrade()
}
fn as_source(source: &Self::SourcePtr) -> BorrowedSource<'_> {
borrow_source(&**source)
}
#[cfg(any(
feature = "tcp",
feature = "udp",
all(feature = "unix", unix),
all(feature = "process", unix)
))]
fn source_ptr<T>(ptr: Rc<T>) -> Rc<dyn AsSource>
where
T: AsSource + Send + Sync + 'static,
{
ptr
}
fn block_on<F>(core: &Rc<Core<Self>>, future: F) -> F::Output
where
F: Future,
{
Core::<Self>::block_on(core, future)
}
fn ensure_progress(_core: &Rc<Core<Self>>) {}
fn set_driven(core: Option<&Rc<Core<Self>>>) {
let core = core.map_or_else(rc::Weak::new, Rc::downgrade);
LOCAL_DRIVEN.with(|driven| replace(driven, core));
}
fn driven() -> Option<Rc<Core<Self>>> {
LOCAL_DRIVEN.with(|driven| driven.borrow().upgrade())
}
}
impl Sealed for Shared {
type Ptr<T> = Arc<T>;
type Weak<T> = sync::Weak<T>;
type Lock<T> = Mutex<T>;
type BoxFuture = Pin<Box<dyn Future<Output = ()> + Send>>;
type HeldWaker = TaskWaker;
type SourcePtr = Arc<dyn AsSource + Send + Sync>;
#[cfg(feature = "helper")]
type Seat = Option<Mutex<crate::driver::Seat>>;
fn new_ptr<T>(value: T) -> Arc<T> {
Arc::new(value)
}
fn into_inner<T>(mut ptr: Arc<T>) -> T {
loop {
match Arc::try_unwrap(ptr) {
Ok(value) => return value,
Err(shared) => ptr = shared,
}
thread::yield_now();
}
}
fn downgrade<T>(ptr: &Arc<T>) -> sync::Weak<T> {
Arc::downgrade(ptr)
}
fn upgrade<T>(weak: &sync::Weak<T>) -> Option<Arc<T>> {
weak.upgrade()
}
fn as_source(source: &Self::SourcePtr) -> BorrowedSource<'_> {
borrow_source(&**source)
}
#[cfg(any(
feature = "tcp",
feature = "udp",
all(feature = "unix", unix),
all(feature = "process", unix)
))]
fn source_ptr<T>(ptr: Arc<T>) -> Arc<dyn AsSource + Send + Sync>
where
T: AsSource + Send + Sync + 'static,
{
ptr
}
fn block_on<F>(core: &Arc<Core<Self>>, future: F) -> F::Output
where
F: Future,
{
#[cfg(feature = "helper")]
if core.seat.is_some() {
return crate::driver::block_on_seated(core, future);
}
Core::<Self>::block_on(core, future)
}
fn ensure_progress(core: &Arc<Core<Self>>) {
core.ensure_progress();
}
fn set_driven(core: Option<&Arc<Core<Self>>>) {
let core = core.map_or_else(sync::Weak::new, Arc::downgrade);
SHARED_DRIVEN.with(|driven| replace(driven, core));
}
fn driven() -> Option<Arc<Core<Self>>> {
SHARED_DRIVEN.with(|driven| driven.borrow().upgrade())
}
}
thread_local! {
static LOCAL_DRIVEN: RefCell<ManuallyDrop<rc::Weak<Core<Local>>>> =
const { RefCell::new(ManuallyDrop::new(rc::Weak::new())) };
static SHARED_DRIVEN: RefCell<ManuallyDrop<sync::Weak<Core<Shared>>>> =
const { RefCell::new(ManuallyDrop::new(sync::Weak::new())) };
}
fn replace<T>(record: &RefCell<ManuallyDrop<T>>, core: T) {
let replaced = mem::replace(&mut *record.borrow_mut(), ManuallyDrop::new(core));
drop(ManuallyDrop::into_inner(replaced));
}
#[cfg(unix)]
fn borrow_source<S>(source: &S) -> BorrowedSource<'_>
where
S: AsSource + ?Sized,
{
source.as_fd()
}
#[cfg(windows)]
fn borrow_source<S>(source: &S) -> BorrowedSource<'_>
where
S: AsSource + ?Sized,
{
source.as_socket()
}
impl<T> Lock<T> for RefCell<T> {
type Guard<'a>
= RefMut<'a, T>
where
T: 'a;
fn new(value: T) -> Self {
RefCell::new(value)
}
fn lock(&self) -> RefMut<'_, T> {
self.borrow_mut()
}
fn get_mut(&mut self) -> &mut T {
RefCell::get_mut(self)
}
}
impl<T> Lock<T> for Mutex<T> {
type Guard<'a>
= MutexGuard<'a, T>
where
T: 'a;
fn new(value: T) -> Self {
Mutex::new(value)
}
fn lock(&self) -> MutexGuard<'_, T> {
Mutex::lock(self).unwrap_or_else(PoisonError::into_inner)
}
fn get_mut(&mut self) -> &mut T {
Mutex::get_mut(self).unwrap_or_else(PoisonError::into_inner)
}
}
}