use std::cell::Cell;
use std::sync::atomic::AtomicBool;
use std::time::{Duration, Instant};
use crate::io::{IoDriver, IoHandle};
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()) };
}
pub(crate) fn install(
world: *mut nexus_rt::World,
io: *mut IoDriver,
timer: *mut TimerDriver,
event_time: *const Cell<Instant>,
shutdown_flag: *const AtomicBool,
) -> 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)),
};
ContextGuard { prev }
}
struct PrevContext {
world: *mut nexus_rt::World,
io: *mut IoDriver,
timer: *mut TimerDriver,
event_time: *const Cell<Instant>,
shutdown: *const AtomicBool,
}
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));
}
}
pub(crate) fn assert_in_runtime(msg: &str) {
let ptr = CTX_WORLD.with(Cell::get);
assert!(!ptr.is_null(), "{msg}");
}
pub fn with_world<R>(f: impl FnOnce(&mut nexus_rt::World) -> R) -> R {
let ptr = CTX_WORLD.with(Cell::get);
assert!(!ptr.is_null(), "with_world() called outside Runtime::block_on");
let world = unsafe { &mut *ptr };
f(world)
}
pub fn with_world_ref<R>(f: impl FnOnce(&nexus_rt::World) -> R) -> R {
let ptr = CTX_WORLD.with(Cell::get);
assert!(!ptr.is_null(), "with_world_ref() called outside Runtime::block_on");
let world = unsafe { &*ptr };
f(world)
}
pub fn io() -> IoHandle {
let ptr = CTX_IO.with(Cell::get);
assert!(!ptr.is_null(), "io() called outside Runtime::block_on");
IoHandle::new(unsafe { &mut *ptr })
}
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)
}
pub fn shutdown_signal() -> crate::ShutdownSignal {
let ptr = CTX_SHUTDOWN.with(Cell::get);
assert!(!ptr.is_null(), "shutdown_signal() called outside Runtime::block_on");
crate::ShutdownSignal { flag: ptr }
}