Files
herdr/src/client/timer.rs
T
Can Celik 207be3c771 refactor: render the shell in the client (#3487)
* feat: add opt-in client-rendered shell

* feat: add client-owned shell mouse controls

* feat: complete client-owned shell chrome

* feat: add client-owned shell settings and notifications

* feat: run client shell custom commands

* feat: render popup terminals in client shell

* feat: add client-owned scrollback and copy mode

* fix: preserve client worktree trust defaults

* feat: render startup diagnostics in client shell

* feat: render onboarding in client shell

* feat: render product announcements in client shell

* feat: render release notes in client shell

* feat: render mobile switcher in client shell

* feat: render kitty graphics in client shell

* fix: preserve client shell lifecycle coherence

* refactor: route client shell commands through endpoint connections

* refactor: make the client-rendered shell the default

* refactor: remove monolithic launch mode

* refactor: complete client-rendered shell cutover

* perf: retain client pane surface updates

* perf: scale retained pane surface updates

* perf: remove remote pane input latency

* test: make client shell checks platform-independent

* fix: prevent client timer starvation

* test: fix client shell platform gates

* test: accept retained surfaces in cross-area checks

* test: make client mode assertions platform-neutral

* fix: badge only outdated integrations
2026-09-01 15:48:22 +03:00

79 lines
2.1 KiB
Rust

use std::time::{Duration, Instant};
pub(super) struct ClientLoopTimer {
deadline: Option<Instant>,
}
impl ClientLoopTimer {
pub(super) fn new() -> Self {
Self { deadline: None }
}
pub(super) fn deadline(&mut self, now: Instant, delay: Duration) -> Instant {
let requested = now.checked_add(delay).unwrap_or(now);
let deadline = self
.deadline
.map_or(requested, |current| current.min(requested));
self.deadline = Some(deadline);
deadline
}
pub(super) fn fired(&mut self) {
self.deadline = None;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn incoming_events_do_not_postpone_timer_deadline() {
let start = Instant::now();
let delay = Duration::from_millis(100);
let mut timer = ClientLoopTimer::new();
let first_deadline = timer.deadline(start, delay);
assert_eq!(
timer.deadline(start + Duration::from_millis(25), delay),
first_deadline
);
assert_eq!(
timer.deadline(start + Duration::from_millis(50), delay),
first_deadline
);
assert_eq!(
timer.deadline(start + Duration::from_millis(75), delay),
first_deadline
);
timer.fired();
assert_eq!(
timer.deadline(first_deadline, delay),
first_deadline + delay
);
}
#[test]
fn earlier_client_work_can_pull_the_timer_deadline_forward() {
let start = Instant::now();
let mut timer = ClientLoopTimer::new();
assert_eq!(
timer.deadline(start, Duration::from_millis(100)),
start + Duration::from_millis(100)
);
assert_eq!(
timer.deadline(start + Duration::from_millis(20), Duration::from_millis(10)),
start + Duration::from_millis(30)
);
assert_eq!(
timer.deadline(
start + Duration::from_millis(25),
Duration::from_millis(100)
),
start + Duration::from_millis(30)
);
}
}