use std::cell::Cell;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;
use std::time::{Duration, Instant};
use crate::io::IoDriver;
use crate::timer::{TimerDriver, TimerHandle};
thread_local! {
static CTX_WORLD: Cell<*mut nexus_rt::World> =
const { Cell::new(std::ptr::null_mut()) };
static CTX_IO: Cell<*mut IoDriver> =
const { Cell::new(std::ptr::null_mut()) };
static CTX_TIMER: Cell<*mut TimerDriver> =
const { Cell::new(std::ptr::null_mut()) };
static CTX_EVENT_TIME: Cell<*const Cell<Instant>> =
const { Cell::new(std::ptr::null()) };
static CTX_SHUTDOWN: Cell<*const AtomicBool> =
const { Cell::new(std::ptr::null()) };
static CTX_SHUTDOWN_WAKER: Cell<*const Arc<std::sync::Mutex<Option<std::task::Waker>>>> =
const { Cell::new(std::ptr::null()) };
}
pub(crate) fn install(
world: *mut nexus_rt::World,
io: *mut IoDriver,
timer: *mut TimerDriver,
event_time: *const Cell<Instant>,
shutdown_flag: *const AtomicBool,
shutdown_waker: *const Arc<std::sync::Mutex<Option<std::task::Waker>>>,
) -> ContextGuard {
let prev = PrevContext {
world: CTX_WORLD.with(|c| c.replace(world)),
io: CTX_IO.with(|c| c.replace(io)),
timer: CTX_TIMER.with(|c| c.replace(timer)),
event_time: CTX_EVENT_TIME.with(|c| c.replace(event_time)),
shutdown: CTX_SHUTDOWN.with(|c| c.replace(shutdown_flag)),
shutdown_waker: CTX_SHUTDOWN_WAKER.with(|c| c.replace(shutdown_waker)),
};
ContextGuard { prev }
}
struct PrevContext {
world: *mut nexus_rt::World,
io: *mut IoDriver,
timer: *mut TimerDriver,
event_time: *const Cell<Instant>,
shutdown: *const AtomicBool,
shutdown_waker: *const Arc<std::sync::Mutex<Option<std::task::Waker>>>,
}
pub(crate) struct ContextGuard {
prev: PrevContext,
}
impl Drop for ContextGuard {
fn drop(&mut self) {
CTX_WORLD.with(|c| c.set(self.prev.world));
CTX_IO.with(|c| c.set(self.prev.io));
CTX_TIMER.with(|c| c.set(self.prev.timer));
CTX_EVENT_TIME.with(|c| c.set(self.prev.event_time));
CTX_SHUTDOWN.with(|c| c.set(self.prev.shutdown));
CTX_SHUTDOWN_WAKER.with(|c| c.set(self.prev.shutdown_waker));
}
}
pub(crate) fn assert_in_runtime(msg: &str) {
let ptr = CTX_WORLD.with(Cell::get);
assert!(!ptr.is_null(), "{msg}");
}
pub(crate) fn current_io_ptr() -> *mut IoDriver {
CTX_IO.with(Cell::get)
}
pub(crate) fn current_world_ptr() -> *mut nexus_rt::World {
CTX_WORLD.with(Cell::get)
}
pub(crate) fn current_shutdown_ptrs() -> (
*const AtomicBool,
*const Arc<std::sync::Mutex<Option<std::task::Waker>>>,
) {
let flag = CTX_SHUTDOWN.with(Cell::get);
let waker = CTX_SHUTDOWN_WAKER.with(Cell::get);
(flag, waker)
}
pub fn sleep(duration: Duration) -> crate::Sleep {
let ptr = CTX_TIMER.with(Cell::get);
assert!(!ptr.is_null(), "sleep() called outside Runtime::block_on");
let handle = TimerHandle::new(unsafe { &mut *ptr });
handle.sleep(duration)
}
pub fn sleep_until(deadline: Instant) -> crate::Sleep {
let ptr = CTX_TIMER.with(Cell::get);
assert!(
!ptr.is_null(),
"sleep_until() called outside Runtime::block_on"
);
let handle = TimerHandle::new(unsafe { &mut *ptr });
handle.sleep_until(deadline)
}
pub fn event_time() -> Instant {
let ptr = CTX_EVENT_TIME.with(Cell::get);
assert!(
!ptr.is_null(),
"event_time() called outside Runtime::block_on"
);
unsafe { &*ptr }.get()
}
pub fn timeout<F: std::future::Future>(duration: Duration, future: F) -> crate::timer::Timeout<F> {
crate::timer::Timeout::new(future, sleep(duration))
}
pub fn interval(period: Duration) -> crate::timer::Interval {
crate::timer::Interval::new(period)
}
pub async fn after<F: std::future::Future>(deadline: Instant, future: F) -> F::Output {
sleep_until(deadline).await;
future.await
}
pub async fn after_delay<F: std::future::Future>(duration: Duration, future: F) -> F::Output {
sleep(duration).await;
future.await
}
pub fn timeout_at<F: std::future::Future>(
deadline: Instant,
future: F,
) -> crate::timer::Timeout<F> {
crate::timer::Timeout::new(future, sleep_until(deadline))
}
pub fn interval_at(start: Instant, period: Duration) -> crate::timer::Interval {
crate::timer::Interval::new_at(start, period)
}
pub fn yield_now() -> crate::timer::YieldNow {
crate::timer::YieldNow(false)
}