use crate::stack::{Stack, pop, push};
use crate::value::Value;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_time_now(stack: Stack) -> Stack {
let micros = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_micros() as i64)
.unwrap_or(0);
unsafe { push(stack, Value::Int(micros)) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_time_nanos(stack: Stack) -> Stack {
let nanos = elapsed_nanos();
unsafe { push(stack, Value::Int(nanos)) }
}
#[inline]
fn elapsed_nanos() -> i64 {
use std::sync::atomic::{AtomicI64, Ordering};
static BASE_NANOS: AtomicI64 = AtomicI64::new(0);
let current = raw_monotonic_nanos();
let base = BASE_NANOS.load(Ordering::Relaxed);
if base != 0 {
return current.saturating_sub(base);
}
match BASE_NANOS.compare_exchange(0, current, Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => 0, Err(actual_base) => current.saturating_sub(actual_base), }
}
#[inline]
#[cfg(unix)]
fn raw_monotonic_nanos() -> i64 {
let mut ts = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe {
libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts);
}
#[allow(clippy::unnecessary_cast)] let secs = (ts.tv_sec as i64).saturating_mul(1_000_000_000);
#[allow(clippy::unnecessary_cast)]
secs.saturating_add(ts.tv_nsec as i64)
}
#[inline]
#[cfg(not(unix))]
fn raw_monotonic_nanos() -> i64 {
use std::sync::OnceLock;
use std::time::Instant;
static BASE: OnceLock<Instant> = OnceLock::new();
let base = BASE.get_or_init(Instant::now);
base.elapsed().as_nanos().try_into().unwrap_or(i64::MAX)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn patch_seq_time_sleep_ms(stack: Stack) -> Stack {
assert!(!stack.is_null(), "time.sleep-ms: stack is empty");
let (rest, value) = unsafe { pop(stack) };
match value {
Value::Int(ms) => {
if ms < 0 {
panic!("time.sleep-ms: duration must be non-negative, got {}", ms);
}
may::coroutine::sleep(Duration::from_millis(ms as u64));
rest
}
_ => panic!(
"time.sleep-ms: expected Int duration on stack, got {:?}",
value
),
}
}
pub use patch_seq_time_nanos as time_nanos;
pub use patch_seq_time_now as time_now;
pub use patch_seq_time_sleep_ms as time_sleep_ms;
#[cfg(test)]
mod tests;