///|
pub(all) struct Backoff {
initial : Int
factor : Int
max_delay : Int
jitter : Int
mut current : Int
}
///|
pub fn Backoff::new(
initial? : Int = 1,
factor? : Int = 2,
max_delay? : Int = 1024,
jitter? : Int = 0,
) -> Backoff {
let safe_initial = if initial < 1 { 1 } else { initial }
{
initial: safe_initial,
factor: if factor < 1 {
1
} else {
factor
},
max_delay: if max_delay < safe_initial {
safe_initial
} else {
max_delay
},
jitter: if jitter < 0 {
0
} else {
jitter
},
current: safe_initial,
}
}
///|
pub fn Backoff::reset(self : Backoff) -> Unit {
self.current = self.initial
}
///|
pub fn Backoff::next_delay(self : Backoff, rng : Rng) -> Int {
let noise = if self.jitter == 0 { 0 } else { rng.next_int(self.jitter + 1) }
let delay = self.current + noise
let next = self.current * self.factor
self.current = if next > self.max_delay { self.max_delay } else { next }
if delay > self.max_delay {
self.max_delay
} else {
delay
}
}
///|
pub(all) struct TimerPlan {
name : String
start_after : Int
interval : Int
times : Int
priority : Int
}
///|
/// Projects a timer plan into the common deterministic event representation.
pub fn TimerPlan::event_stream(self : TimerPlan) -> EventStream {
let stream = EventStream::new()
let mut index = 0
while index < self.times {
ignore(
stream.record(
timer_event_kind(),
self.start_after + index * self.interval,
"timer.fire",
correlation_id=self.name,
source="timer",
target=self.name,
payload="occurrence=" + (index + 1).to_string(),
priority=self.priority,
),
)
index += 1
}
stream
}
///|
pub fn timer_plan(
name : String,
start_after? : Int = 0,
interval? : Int = 1,
times? : Int = 1,
priority? : Int = 0,
) -> TimerPlan {
{
name,
start_after: if start_after < 0 {
0
} else {
start_after
},
interval: if interval < 1 {
1
} else {
interval
},
times: if times < 1 {
1
} else {
times
},
priority,
}
}
///|
pub fn Sim::install_timer(self : Sim, plan : TimerPlan) -> Int {
self.schedule_repeating(
plan.start_after,
plan.interval,
plan.times,
plan.name,
priority=plan.priority,
)
}
///|
pub fn Sim::schedule_timeout(self : Sim, after : Int, name : String) -> Int {
self.schedule_after(after, "timeout:" + name, priority=-100)
}
///|
pub fn Sim::schedule_backoff(
self : Sim,
backoff : Backoff,
name : String,
) -> Int {
let delay = backoff.next_delay(self.rng)
self.record(0, "timer.backoff", name + "=" + delay.to_string())
self.schedule_after(delay, name)
}