Module sys/timer
Async sleep (sleep) — the raw timer boundary.
This is the PLUMBING layer: it speaks whole milliseconds and raw errno,
matching the platform timer underneath. Application code should use
std/time/sleep's sleep(Duration, io), which wraps this; std/async
also reaches it for its own timeouts (std/async/index.yo,
std/async/channel.yo, std/async/mutex.yo) and so does the compiler's
yo check --watch loop.
Stability
unstable — the unit is the open question. A millisecond u64 is what
timerfd, kqueue and the Windows threadpool all accept, so it is the
honest shape for this layer, but it means every sub-millisecond duration
silently rounds and std/time/sleep has to do the Duration conversion
on the way in. There is also no cancellation: the returned future has no
way to be dropped early, which is what a timeout-shaped API needs.
Freezing follows those two — a resolution the platforms agree on
(nanoseconds, truncating) and a cancel path. std/time/sleep is the
stable surface meanwhile.
Functions
Suspend the current async task for milliseconds, without blocking the
event loop — the one operation in std/sys that exists purely to yield
time back to the loop.
Resolves to 8 on success on every platform (historically the size of a
timerfd read; the value carries no information) or a negative errno.
Awaiting it is what arms the timer's completion; a future that is created
and never awaited still holds the loop open until it fires.
Each platform uses its native timer. On Linux, io_uring uses one
IORING_OP_TIMEOUT with no descriptor, and the epoll fallback (a kernel
or sandbox without io_uring, such as Docker's default seccomp profile)
uses a timerfd in its epoll set. macOS uses a kqueue EVFILT_TIMER,
Windows threadpool timers, and wasm a sorted timer queue the loop drains.
If Linux can create neither backend, the sleep completes at once with that
errno instead of exiting the process (plans/reference/DROP_LIBURING.md §3.6).
Parameters
| Name | Type | Notes |
|---|---|---|
milliseconds | u64 |
Returns: IoFuture