Skip to content

Shouldn't this also be paired with a monotonic time opcode? #230

Open

Description

@dead-claudia

Sleep timers are normally batched for performance, with durations turned into deadlines, and effective batching requires tracking a monotonic clock to know precisely how long to wait for. In fact, processors normally mandate such batching at the operating system level. And this primitive is implemented on every platform, from embedded CPUs to high-performance operating systems, as it's a strict necessity.

Here's the API on various relevant platforms:

  • Operating systems:
    • Most Unix-like: clock_gettime(CLOCK_MONOTONIC, &tv);
    • Linux: clock_gettime(CLOCK_BOOTTIME, &tv);
    • Windows: QueryInterruptTime(&ticks);
  • The ACPI timer can be used as a clock reference even on older desktops.
  • Processors:
    • x86, x86-64: HPET, rdtsc * base frequency on modern processors
    • ARM: Generic Timer system register
    • RISC-V: memory-mapped mtime
    • AVR doesn't natively, but real-time clock peripherals are pervasive, and Arduinos have them built-in.
    • Others also have their various ways - just look at the Linux VDSO clock_gettime implementations for various architectures.

I propose something like an atomic.clock.get that returns an i64 nanosecond count, to align with the existing wait opcodes.

Activity

  1. dschuff commented on May 5, 2025

    @dschuff
    Member

    I think in a virtual system like Wasm, monotonic time sounds more like an API than an instruction. Indeed in JS we have Performance.now() which is monotonic, and it looks like wasi-clocks is what you'd want elsewhere.

  2. dead-claudia commented on May 5, 2025

    @dead-claudia
    Author

    @dschuff Similar could be said about 80% of https://github.com/WebAssembly/shared-everything-threads and 100% of https://github.com/WebAssembly/stringref, by the way.

    Not sure where the line should be drawn with that, if it shouldn't be drawn before either of those.

  3. sunfishcode commented on May 5, 2025

    @sunfishcode
    Member

    Another potential angle here is that threads with shared memory can already be used to build timers: spawn a thread and have it spin incrementing an integer value in a shared memory location. Defining an instruction doesn't add anything you can't already do; it'd just be much more efficient, less noisy (spectre mitigations aside), and more portable (if we chose to specify the clock resolution).

  4. dead-claudia commented on May 5, 2025

    @dead-claudia
    Author

    @sunfishcode Is that supposed to be a point for the idea or a point against the idea? Or is it just a general remark? 😕

  5. tlively commented on May 5, 2025

    @tlively
    Member

    Notably we have (so far) drawn the line before stringref by shipping https://github.com/WebAssembly/js-string-builtins instead :) Shared-everything threads is modeled after this initial threads proposal in what it provides in the language and what it requires to be imported APIs.

  6. dead-claudia commented on May 5, 2025

    @dead-claudia
    Author

    I'll also point out that timed conditional waits and notifies for those waiters have traditionally not been implemented at the bytecode level, in language VMs or in hardware ISAs. Language VMs almost always rely on library calls, while hardware normally exposes either a tick counter + value to send an interrupt (like RISC-V) or a number of ticks before interrupt (like ARM). The BEAM VM is a rare exception, but even then, it's a timeout to receive a value, not a timeout to detect a change.

    Language VMs also typically implement monotonic timestamp access as functions, while hardware ISAs implement them natively as either internal registers or memory-mapped registers.

  7. sunfishcode commented on May 6, 2025

    @sunfishcode
    Member

    @dead-claudia My comment above sketches a possible argument for adding a new instruction: by arguing that it's useful while perhaps not adding a new capability. I hypothesize that "does it add a capability?" is ultimately the main concrete question underneath the question "does it sound like a library API?".

    That said, an argument against adding a new instruction is that if it defines the result to represent milliseconds or any other fixed quantity, it would introduce the first physical unit to the core spec shared by all Wasm users. That's not something to do lightly. For example, I expect 1 millisecond is too coarse-grained for some use cases, while not all devices support nanoseconds.

    As another argument against, consider a Wasm engine that can take a snapshot of a running Wasm instance and save it to a Wasm module that can later be instantiated to resume execution. Such an engine wouldn't be able to ensure monotonicity across a suspend/resume in the presence of a monotonic clock instruction. Perhaps that could be addressed by adding a second instruction that increments the monotonic timer to a specified value, though that would add more complexity.

  8. dead-claudia commented on May 6, 2025

    @dead-claudia
    Author

    That said, an argument against adding a new instruction is that if it defines the result to represent milliseconds or any other fixed quantity, it would introduce the first physical unit to the core spec shared by all Wasm users. That's not something to do lightly. For example, I expect 1 millisecond is too coarse-grained for some use cases, while not all devices support nanoseconds.

    @sunfishcode Sorry, I meant nanoseconds. Intent was first to align with the wait opcodes, and any further justification was intended to just build on top of that. Fixed the proposal in this issue to reflect that.

  9. sunfishcode commented on May 15, 2025

    @sunfishcode
    Member

    Ah, I had missed that the wait opcode already has a timeout in nanoseconds. That's indeed a precedent for using nanoseconds here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions