1/** @Resilience.Clock.Seam — monotonic time and cancellable waiting */2modulePuduLangResilience.Clock34importStd.Concurrent.CancelasCancel5importStd.SyncasSync6importStd.TimeasTime7importPuduLangResilience.Utils.NumericasNumeric8importPuduLangResilience.Utils.SharedasShared910/** @Resilience.Clock.Clock — reads time and waits in milliseconds */11exporttypeClock = {12 now: fn() -> Int,13 sleep: fn(Int, Cancel.Token) -> Result[(), Cancel.Reason]14}1516/** @Resilience.Clock.Manual — time that moves only when told */17exporttypeManual = { time: Shared.Shared[Int], slept: Shared.Shared[Array[Int]] }1819/// The monotonic clock of the running program. Waiting ends early when the token fires.20exportfnsystem() -> Clock {21Clock {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.28exportfnmanual(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.32exportfnofManual(source: &Manual) -> Clock {33let time = source.time34let slept = source.slept35Clock {36 now: fn() -> Int { Shared.current(&time) },37 sleep: fn(millis: Int, token: Cancel.Token) -> Result[(), Cancel.Reason] {38Cancel.check(&token) ?39let wait = if millis < 0 { 0 } else { millis }40Shared.update(&slept, fn(waits: Array[Int]) -> Array[Int] { waits.push(wait) })41Shared.update(&time, fn(held: Int) -> Int { Numeric.add(held, wait) })42Ok(())43 }44 }45}4647/// Moves a manual clock forward by `millis`; a negative amount moves it by nothing.48exportfnadvance(target: &Manual, millis: Int) -> () {49let step = if millis < 0 { 0 } else { millis }50Shared.update(&target.time, fn(held: Int) -> Int { Numeric.add(held, step) })51}5253/// Every wait requested of a manual clock, oldest first.54exportfnsleeps(source: &Manual) -> Array[Int] { Shared.current(&source.slept) }5556/// The clock's reading, in milliseconds.57exportfnnow(source: &Clock) -> Int { (source.now)() }5859/// Waits `millis` on the clock, or until the token fires; the token's reason when it fired.60exportfnsleep(source: &Clock, millis: Int, token: &Cancel.Token) -> Result[(), Cancel.Reason] {61 (source.sleep)(millis, *token)62}63