///|
/// Clock package for P0-CLOCK-KERNEL.
///|
pub fn package_id() -> String {
"lockwire/clock"
}
///|
pub(all) enum DomainId {
DomainId(String)
} derive(Eq, Debug)
///|
pub fn DomainId::new(name~ : String) -> DomainId {
DomainId(name)
}
///|
pub fn DomainId::name(self : DomainId) -> String {
match self {
DomainId(name) => name
}
}
///|
pub(all) enum SimTruth {
SimTruth
} derive(Eq, Debug)
///|
pub(all) enum DcDomain {
DcDomain
} derive(Eq, Debug)
///|
pub(all) enum WallDomain {
WallDomain
} derive(Eq, Debug)
///|
pub struct Instant[D] {
priv domain : DomainId
priv ns : Int64
priv phantom : D?
}
///|
pub fn[D] Instant::new(domain~ : DomainId, ns~ : Int64) -> Instant[D] {
{ domain, ns, phantom: None }
}
///|
pub fn[D] Instant::domain(self : Instant[D]) -> DomainId {
ignore(self.phantom)
self.domain
}
///|
pub fn[D] Instant::ns(self : Instant[D]) -> Int64 {
self.ns
}
///|
pub fn[D] Instant::add(self : Instant[D], delta : @core.Duration) -> Instant[D] {
Instant::new(domain=self.domain, ns=self.ns + delta.ns())
}
///|
pub fn[D] Instant::duration_since(
self : Instant[D],
earlier : Instant[D],
) -> @core.Duration {
@core.Duration::from_ns(self.ns - earlier.ns)
}
///|
pub fn[D] Instant::equals(self : Instant[D], other : Instant[D]) -> Bool {
self.domain == other.domain && self.ns == other.ns
}
///|
pub type SimInstant = Instant[SimTruth]
///|
pub(all) struct DomainInstant {
domain : DomainId
ns : Int64
} derive(Eq, Debug)
///|
pub fn DomainInstant::new(domain~ : DomainId, ns~ : Int64) -> DomainInstant {
{ domain, ns }
}
///|
pub fn[D] DomainInstant::from_typed(instant : Instant[D]) -> DomainInstant {
{ domain: instant.domain(), ns: instant.ns() }
}
///|
pub fn DomainInstant::domain(self : DomainInstant) -> DomainId {
self.domain
}
///|
pub fn DomainInstant::ns(self : DomainInstant) -> Int64 {
self.ns
}
///|
pub(all) struct Uncertain[T] {
value : T
bound : @core.Duration
}
///|
pub struct ClockRelation[A, B] {
priv source_domain : DomainId
priv target_domain : DomainId
priv offset : @core.Duration
priv rate_ppb : Int64
priv bound : @core.Duration
priv asof : Instant[A]
priv target_phantom : B?
}
///|
pub fn[A, B] ClockRelation::new(
source_domain~ : DomainId,
target_domain~ : DomainId,
offset~ : @core.Duration,
rate_ppb~ : Int64,
bound~ : @core.Duration,
asof~ : Instant[A],
) -> ClockRelation[A, B] {
{
source_domain,
target_domain,
offset,
rate_ppb,
bound,
asof,
target_phantom: None,
}
}
///|
pub fn[A, B] ClockRelation::source_domain(
self : ClockRelation[A, B],
) -> DomainId {
self.source_domain
}
///|
pub fn[A, B] ClockRelation::target_domain(
self : ClockRelation[A, B],
) -> DomainId {
ignore(self.target_phantom)
self.target_domain
}
///|
pub fn[A, B] ClockRelation::bound(self : ClockRelation[A, B]) -> @core.Duration {
self.bound
}
///|
pub fn[A, B] ClockRelation::convert(
self : ClockRelation[A, B],
instant : Instant[A],
) -> Uncertain[Instant[B]] {
ignore(self.target_phantom)
let elapsed = instant.duration_since(self.asof).ns()
let drift = elapsed * self.rate_ppb / 1_000_000_000L
let converted : Instant[B] = Instant::new(
domain=self.target_domain,
ns=instant.ns() + self.offset.ns() + drift,
)
{ value: converted, bound: self.bound }
}
///|
pub(all) struct CrossStamp[A, B] {
source : Instant[A]
target : Instant[B]
bound : @core.Duration
}
///|
pub(all) struct DomainCrossStamp {
source : DomainInstant
target : DomainInstant
bound : @core.Duration
} derive(Eq, Debug)
///|
pub(open) trait MonotonicClock {
fn now(Self) -> DomainInstant
fn domain(Self) -> DomainId
fn resolution(Self) -> @core.Duration
}
///|
pub(open) trait TimerDriver {
fn sleep_until(Self, DomainInstant) -> Unit
}
///|
pub(open) trait CrossStamper {
fn cross_stamp(Self) -> DomainCrossStamp
}
///|
pub struct VirtualClock[D] {
priv domain : DomainId
priv mut current : Instant[D]
priv resolution : @core.Duration
priv seed : Int
}
///|
pub fn[D] VirtualClock::new(domain~ : DomainId, seed~ : Int) -> VirtualClock[D] {
{
domain,
current: Instant::new(domain~, ns=0L),
resolution: @core.Duration::from_ns(1L),
seed,
}
}
///|
pub fn[D] VirtualClock::seed(self : VirtualClock[D]) -> Int {
self.seed
}
///|
pub fn[D] VirtualClock::now(self : VirtualClock[D]) -> Instant[D] {
self.current
}
///|
pub fn[D] VirtualClock::domain(self : VirtualClock[D]) -> DomainId {
self.domain
}
///|
pub fn[D] VirtualClock::resolution(self : VirtualClock[D]) -> @core.Duration {
self.resolution
}
///|
pub fn[D] VirtualClock::set(
self : VirtualClock[D],
instant : Instant[D],
) -> Unit {
if instant.domain() == self.domain {
self.current = instant
} else {
self.current = Instant::new(domain=self.domain, ns=instant.ns())
}
}
///|
pub fn[D] VirtualClock::sleep_until(
self : VirtualClock[D],
deadline : Instant[D],
) -> Unit {
if self.now().ns() < deadline.ns() {
self.set(deadline)
}
}
///|
pub fn[D] VirtualClock::sleep_until_domain(
self : VirtualClock[D],
deadline : DomainInstant,
) -> Unit {
if self.domain == deadline.domain() && self.now().ns() < deadline.ns() {
self.set(Instant::new(domain=self.domain, ns=deadline.ns()))
}
}
///|
pub fn[D] VirtualClock::advance(
self : VirtualClock[D],
delta : @core.Duration,
) -> Instant[D] {
self.current = self.current.add(delta)
self.current
}
///|
pub impl[D] MonotonicClock for VirtualClock[D] with fn now(self) {
DomainInstant::from_typed(VirtualClock::now(self))
}
///|
pub impl[D] MonotonicClock for VirtualClock[D] with fn domain(self) {
VirtualClock::domain(self)
}
///|
pub impl[D] MonotonicClock for VirtualClock[D] with fn resolution(self) {
VirtualClock::resolution(self)
}
///|
pub impl[D] TimerDriver for VirtualClock[D] with fn sleep_until(self, deadline) {
self.sleep_until_domain(deadline)
}
///|
pub struct VirtualCrossStamper[A, B] {
priv source : VirtualClock[A]
priv relation : ClockRelation[A, B]
}
///|
pub fn[A, B] VirtualCrossStamper::new(
source~ : VirtualClock[A],
relation~ : ClockRelation[A, B],
) -> VirtualCrossStamper[A, B] {
{ source, relation }
}
///|
pub fn[A, B] VirtualCrossStamper::typed_cross_stamp(
self : VirtualCrossStamper[A, B],
) -> CrossStamp[A, B] {
let source = self.source.now()
let target = self.relation.convert(source)
{ source, target: target.value, bound: target.bound }
}
///|
pub impl[A, B] CrossStamper for VirtualCrossStamper[A, B] with fn cross_stamp(
self,
) {
let typed = self.typed_cross_stamp()
{
source: DomainInstant::from_typed(typed.source),
target: DomainInstant::from_typed(typed.target),
bound: typed.bound,
}
}
///|
pub fn[D] deterministic_sequence(
domain~ : DomainId,
seed~ : Int,
count~ : Int,
step~ : @core.Duration,
) -> Array[Instant[D]] {
let clock : VirtualClock[D] = VirtualClock::new(domain~, seed~)
let out : Array[Instant[D]] = []
for _ in 0.. String {
"instant-ghost-domain-supported; trait-domainid-fallback"
}