Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-resilience

Resilience pipelines for Pudu: retry, circuit breaker, timeout, fallback, hedging, rate limiting, and chaos injection

0.1.0Apache-2.01

InstallClose

Clock.pudu

Pudu63 lines2.4 KB

GitHub ↗
1/** @Resilience.Clock.Seam — monotonic time and cancellable waiting */2module PuduLangResilience.Clock34import Std.Concurrent.Cancel as Cancel5import Std.Sync as Sync6import Std.Time as Time7import PuduLangResilience.Utils.Numeric as Numeric8import PuduLangResilience.Utils.Shared as Shared910/** @Resilience.Clock.Clock — reads time and waits in milliseconds */11export type Clock = {12  now: fn() -> Int,13  sleep: fn(Int, Cancel.Token) -> Result[(), Cancel.Reason]14}1516/** @Resilience.Clock.Manual — time that moves only when told */17export type Manual = { time: Shared.Shared[Int], slept: Shared.Shared[Array[Int]] }1819/// The monotonic clock of the running program. Waiting ends early when the token fires.20export fn system() -> Clock {21  Clock {22    now: fn() -> Int { Time.elapsed() },23    sleep: fn(millis: Int, token: Cancel.Token) -> Result[(), Cancel.Reason] { Cancel.pause(&token, millis) }24  }25}2627/// A manual clock reading `start` milliseconds until it is advanced.28export fn manual(start: Int) -> Manual { Manual{time: Shared.shared(start), slept: Shared.shared([])} }2930/// A clock driven by a manual clock. Waiting records the requested wait and advances the manual31/// clock by it at once; it answers the token's reason when the token has fired.32export fn ofManual(source: &Manual) -> Clock {33  let time = source.time34  let slept = source.slept35  Clock {36    now: fn() -> Int { Shared.current(&time) },37    sleep: fn(millis: Int, token: Cancel.Token) -> Result[(), Cancel.Reason] {38      Cancel.check(&token) ?39      let wait = if millis < 0 { 0 } else { millis }40      Shared.update(&slept, fn(waits: Array[Int]) -> Array[Int] { waits.push(wait) })41      Shared.update(&time, fn(held: Int) -> Int { Numeric.add(held, wait) })42      Ok(())43    }44  }45}4647/// Moves a manual clock forward by `millis`; a negative amount moves it by nothing.48export fn advance(target: &Manual, millis: Int) -> () {49  let step = if millis < 0 { 0 } else { millis }50  Shared.update(&target.time, fn(held: Int) -> Int { Numeric.add(held, step) })51}5253/// Every wait requested of a manual clock, oldest first.54export fn sleeps(source: &Manual) -> Array[Int] { Shared.current(&source.slept) }5556/// The clock's reading, in milliseconds.57export fn now(source: &Clock) -> Int { (source.now)() }5859/// Waits `millis` on the clock, or until the token fires; the token's reason when it fired.60export fn sleep(source: &Clock, millis: Int, token: &Cancel.Token) -> Result[(), Cancel.Reason] {61  (source.sleep)(millis, *token)62}63