Silk

silk/monotonic_clock

Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc.

Provider-replaceable monotonic marks, resolution, and deadline waits.

When to use

Require MonotonicClock to measure elapsed time or wait for a provider-local deadline. Use silk.system_clock when a value must correspond to a human date or external Unix timestamp.

Details

A monotonic mark uses Instant with an unspecified origin. Successive reads from one unchanged provider are non-decreasing, although equal marks are permitted. Marks and absolute deadlines are meaningful only on the logical provider timeline that produced them.

Gotchas

The shared Instant representation cannot statically prevent mixing system time or marks from different providers. Native OS waits block the calling host thread and do not park one Fiber; scheduler-owned tasks receive a lexical replacement whose waits park only that task. Virtual providers may satisfy a wait by advancing their own timeline without sleeping. An invalid or unrepresentable provider result traps because this service has no typed failure channel.

Examples

Advance a virtual clock through lexical provision

import silk.effect {Effect}

import silk.monotonic_clock {MonotonicClock}

import silk.system_clock {SystemClock, Instant}

struct VirtualClock {
  mark: Instant
}

impl MonotonicClock for VirtualClock {
  effect fn now(self: &mut Self) -> Instant {
    return SystemClock.make(SystemClock.seconds(&self.mark), SystemClock.nanoseconds(&self.mark))
  }
  effect fn getResolution(self: &mut Self) -> u64 {
    return 1
  }
  effect fn waitUntil(self: &mut Self, when: Instant) -> () {
    let seconds = SystemClock.seconds(&when)
    let currentSeconds = SystemClock.seconds(&self.mark)
    if seconds > currentSeconds || (seconds == currentSeconds && SystemClock.nanoseconds(&when) > SystemClock.nanoseconds(
      &self.mark,
    )) {
      self.mark = move when
    }
    return ()
  }
  effect fn waitFor(self: &mut Self, howLong: u64) -> () {
    self.mark = MonotonicClock.deadlineAfter(&self.mark, howLong)
    return ()
  }
}

pub fn main() -> i32 {
  let mut clock = VirtualClock {mark: SystemClock.make(0, 0)}
  run MonotonicClock.waitFor(300ms)
    |> Effect.provideMut<MonotonicClock>(&mut clock)
  if SystemClock.nanoseconds(&clock.mark) == 300000000 {
    return 42
  }
  return 0
}

Import as MonotonicClock with import silk.monotonic_clock { MonotonicClock }.

Public declarations: 1.

MonotonicClock

pub service MonotonicClock

An explicit provider of monotonic marks, nominal resolution, and deadline waits.

Details

Exclusive access permits deterministic providers to advance their timeline and record waits. Every operation belongs to the same logical provider timeline.

Operation now

effect<'static> fn now() -> Instant ? &mut MonotonicClock

Reads a canonical mark on the active provider's unspecified timeline. Successive reads are non-decreasing but may be equal.

Operation getResolution

effect<'static> fn getResolution() -> u64 ? &mut MonotonicClock

Returns the provider-reported positive nominal resolution in whole nanoseconds. The value may be a lower bound on actual observable precision.

Operation waitUntil

effect<'static> fn waitUntil(when: Instant) -> () ? &mut MonotonicClock

Waits until the provider's logical mark is at least when.

Gotchas

when must be a canonical mark from the same logical provider. A reached or past deadline returns immediately. The official native provider blocks its calling host thread.

Operation waitFor

effect<'static> fn waitFor(howLong: u64) -> () ? &mut MonotonicClock

Waits for at least howLong nanoseconds on the provider's logical timeline.

Gotchas

A zero duration requires no positive timeline advance. An implementation traps rather than wrapping when the derived absolute deadline cannot be represented.

Associated function MonotonicClock.deadlineAfter

pub fn deadlineAfter<'life0>(start: &'life0 silk/system_clock.Instant, howLong: u64) -> Instant

Derives the canonical absolute deadline howLong nanoseconds after start.

Details

The addition is performed in split seconds and nanoseconds so every u64 duration is accepted whenever the resulting signed-seconds Instant remains representable. Fractional overflow carries exactly once into seconds.

Gotchas

This operation traps rather than wrapping when the resulting whole seconds exceed the representable i64 range. start must belong to the provider timeline on which the deadline will be used.

On this page