//! Time handling and async sleeps.
//!
//! `sleep` is implemented without threads or platform timer backends: on
//! native targets a [`Sleep`] future registers a deadline in a thread-local
//! timer list. Each call to [`crate::rt::poll_step`] reports the earliest
//! deadline back to the host as `PollStatus::Idle::next_deadline`, so the
//! host can sleep at most that long before driving the runtime again. On
//! WASM the host's `timer_set` import schedules the wake-up directly.
#![cfg_attr(
target_family = "wasm",
allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_sign_loss,
clippy::missing_const_for_fn,
)
)]
/// Re-export `core::time::Duration` as the canonical duration type.
pub use core::time::Duration;
// ─── Native Instant
// ───────────────────────────────────────────────────────────
#[cfg(not(target_family = "wasm"))]
mod native_instant {
use core::time::Duration;
// ── Unix: clock_gettime(CLOCK_MONOTONIC)
// ─────────────────────────────────
#[cfg(unix)]
#[repr(C)]
struct Timespec {
tv_sec: i64,
tv_nsec: i64,
}
#[cfg(unix)]
unsafe extern "C" {
fn clock_gettime(clk_id: i32, tp: *mut Timespec) -> i32;
}
#[cfg(unix)]
const CLOCK_MONOTONIC: i32 = 1;
// ── Windows: QueryPerformanceCounter
// ────────────────────────────────────
#[cfg(windows)]
unsafe extern "system" {
fn QueryPerformanceCounter(lp_performance_count: *mut i64) -> i32;
fn QueryPerformanceFrequency(lp_frequency: *mut i64) -> i32;
}
#[cfg(windows)]
static QPC_FREQ: core::sync::atomic::AtomicI64 = core::sync::atomic::AtomicI64::new(0);
#[cfg(windows)]
fn qpc_freq() -> i64 {
use core::sync::atomic::Ordering;
let cached = QPC_FREQ.load(Ordering::Relaxed);
if cached != 0 {
return cached;
}
let mut f = 0i64;
// SAFETY: pointer is valid for the call duration.
unsafe { QueryPerformanceFrequency(&mut f) };
QPC_FREQ.store(f, Ordering::Relaxed);
f
}
/// A measurement of a monotonic clock, in nanoseconds since an
/// unspecified epoch.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Instant(u64);
impl Instant {
/// Returns the current instant.
pub fn now() -> Self {
#[cfg(unix)]
{
let mut ts = Timespec {
tv_sec: 0,
tv_nsec: 0,
};
// SAFETY: ts is valid for the call duration.
unsafe { clock_gettime(CLOCK_MONOTONIC, &mut ts) };
let nanos = (ts.tv_sec as u64)
.wrapping_mul(1_000_000_000)
.wrapping_add(ts.tv_nsec as u64);
Self(nanos)
}
#[cfg(windows)]
{
let mut count = 0i64;
// SAFETY: pointer is valid for the call duration.
unsafe { QueryPerformanceCounter(&mut count) };
let freq = qpc_freq();
// Convert ticks → nanoseconds.
let nanos = if freq > 0 {
(count as u128).wrapping_mul(1_000_000_000) / freq as u128
} else {
0
};
Self(nanos as u64)
}
#[cfg(not(any(unix, windows)))]
{
Self(0)
}
}
/// Returns the amount of time elapsed from `earlier` to `self`.
#[must_use]
pub fn duration_since(&self, earlier: Self) -> Duration {
Duration::from_nanos(self.0.saturating_sub(earlier.0))
}
/// Returns the time elapsed since this instant.
#[must_use]
pub fn elapsed(&self) -> Duration {
Self::now().duration_since(*self)
}
}
impl core::ops::Sub for Instant {
type Output = Duration;
fn sub(self, rhs: Self) -> Duration {
self.duration_since(rhs)
}
}
impl core::ops::Add<Duration> for Instant {
type Output = Self;
fn add(self, rhs: Duration) -> Self {
Self(self.0.saturating_add(rhs.as_nanos() as u64))
}
}
}
#[cfg(not(target_family = "wasm"))]
pub use native_instant::Instant;
// ─── Native SystemTime
// ────────────────────────────────────────────────────────
/// A simple wall-clock timestamp (nanoseconds since the Unix epoch).
#[cfg(not(target_family = "wasm"))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct SystemTime(pub u64);
/// Error returned when a `SystemTime` subtraction underflows.
#[cfg(not(target_family = "wasm"))]
#[derive(Debug)]
pub struct SystemTimeError;
#[cfg(not(target_family = "wasm"))]
impl core::fmt::Display for SystemTimeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("system time error")
}
}
// ─── Native Sleep future
// ──────────────────────────────────────────────────────
#[cfg(not(target_family = "wasm"))]
mod native_time {
use super::Instant;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};
use core::time::Duration;
/// Future that resolves once a target [`Instant`] has been reached.
pub struct Sleep {
deadline: Instant,
timer_id: Option<u64>,
}
impl Sleep {
fn new(duration: Duration) -> Self {
Self {
deadline: Instant::now() + duration,
timer_id: None,
}
}
}
impl Future for Sleep {
type Output = ();
fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
if Instant::now() >= self.deadline {
if let Some(id) = self.timer_id.take() {
crate::rt::timers::cancel_timer(id);
}
return Poll::Ready(());
}
if self.timer_id.is_none() {
self.timer_id = Some(crate::rt::timers::register_timer(self.deadline));
}
Poll::Pending
}
}
impl Drop for Sleep {
fn drop(&mut self) {
if let Some(id) = self.timer_id.take() {
crate::rt::timers::cancel_timer(id);
}
}
}
/// Sleep asynchronously for `duration`.
pub fn sleep(duration: Duration) -> Sleep {
Sleep::new(duration)
}
}
#[cfg(not(target_family = "wasm"))]
pub use native_time::{Sleep, sleep};
// ─── WASM
// ─────────────────────────────────────────────────────────────────────
#[cfg(target_family = "wasm")]
use crate::abi::imports;
#[cfg(target_family = "wasm")]
use crate::rt::wasm::OverlappedFuture;
/// Sleep asynchronously for `duration` (WASM host-scheduled timer).
#[cfg(target_family = "wasm")]
pub async fn sleep(duration: Duration) {
let delay_ms = duration.as_millis() as u32;
let _ = OverlappedFuture::new(move |ov| {
// SAFETY: `ov` is a valid overlapped pointer supplied by the runtime.
unsafe { imports::timer_set(ov, delay_ms) };
})
.await;
}
/// A measurement of the host's monotonic clock (milliseconds since an epoch).
#[cfg(target_family = "wasm")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Instant(u64);
#[cfg(target_family = "wasm")]
impl Instant {
/// Returns the current instant from the WASM host clock.
pub fn now() -> Self {
// SAFETY: `get_time` is a side-effect-free host import.
Self(unsafe { imports::get_time() as u64 })
}
/// Returns the duration elapsed from `earlier` to `self`.
#[must_use]
pub fn duration_since(&self, earlier: Self) -> Duration {
Duration::from_millis(self.0.saturating_sub(earlier.0))
}
/// Returns the time elapsed since this instant was created.
#[must_use]
pub fn elapsed(&self) -> Duration {
Self::now().duration_since(*self)
}
}
#[cfg(target_family = "wasm")]
impl core::ops::Add<Duration> for Instant {
type Output = Self;
fn add(self, rhs: Duration) -> Self {
Self(self.0.saturating_add(rhs.as_millis() as u64))
}
}
#[cfg(target_family = "wasm")]
impl core::ops::Sub for Instant {
type Output = Duration;
fn sub(self, rhs: Self) -> Duration {
self.duration_since(rhs)
}
}
/// Wall-clock timestamp (nanoseconds since Unix epoch).
#[cfg(target_family = "wasm")]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct SystemTime(pub u64);
/// Error returned when a `SystemTime` subtraction underflows.
#[cfg(target_family = "wasm")]
#[derive(Debug)]
pub struct SystemTimeError;
#[cfg(target_family = "wasm")]
impl core::fmt::Display for SystemTimeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("system time error")
}
}