gen_server: idle/receive timeout via select_timeout/recv_timeout + handle_idle (RFC 015 §4.4)
This commit is contained in:
@@ -580,3 +580,69 @@ fn cancel_stops_a_periodic() {
|
||||
// The periodic had fired at least once before being cancelled.
|
||||
assert!(!fired.lock().unwrap().is_empty());
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// RFC 015 §4.4 — idle / receive timeout. A server that sets an idle window in
|
||||
// init and counts handle_idle fires; casts are traffic that resets the window.
|
||||
// ---------------------------------------------------------------------------
|
||||
struct Idler {
|
||||
window: Duration,
|
||||
idles: Arc<Mutex<u32>>,
|
||||
}
|
||||
|
||||
impl GenServer for Idler {
|
||||
type Call = ();
|
||||
type Reply = u32; // idle fire count
|
||||
type Cast = (); // a poke: traffic that resets the idle window
|
||||
type Info = ();
|
||||
type Timer = ();
|
||||
|
||||
fn init(&mut self, ctx: &ServerCtx<Self>) {
|
||||
ctx.idle_after(self.window);
|
||||
}
|
||||
fn handle_call(&mut self, _: ()) -> u32 {
|
||||
*self.idles.lock().unwrap()
|
||||
}
|
||||
fn handle_cast(&mut self, _: ()) {}
|
||||
fn handle_idle(&mut self) {
|
||||
*self.idles.lock().unwrap() += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// A quiet inbox fires handle_idle, and the window re-arms (steady detector):
|
||||
// several fires across a quiet span.
|
||||
#[test]
|
||||
fn idle_fires_repeatedly_on_quiet() {
|
||||
let idles = Arc::new(Mutex::new(0));
|
||||
let i2 = idles.clone();
|
||||
run(move || {
|
||||
let server = start(Idler { window: Duration::from_millis(25), idles: i2 });
|
||||
smarm::sleep(Duration::from_millis(130)); // quiet ⇒ ~5 windows
|
||||
drop(server); // keep the server alive across the quiet span
|
||||
});
|
||||
assert!(*idles.lock().unwrap() >= 2, "idle should re-arm and fire several times");
|
||||
}
|
||||
|
||||
// Traffic within the window keeps idle from firing; only once the inbox goes
|
||||
// quiet does handle_idle fire.
|
||||
#[test]
|
||||
fn traffic_resets_the_idle_window() {
|
||||
let idles = Arc::new(Mutex::new(0));
|
||||
let i2 = idles.clone();
|
||||
let before_quiet = Arc::new(Mutex::new(u32::MAX));
|
||||
let bq = before_quiet.clone();
|
||||
run(move || {
|
||||
let server = start(Idler { window: Duration::from_millis(60), idles: i2 });
|
||||
// Poke every 25ms (< 60ms window) for ~100ms: each cast resets the
|
||||
// window before it can elapse.
|
||||
for _ in 0..4 {
|
||||
server.cast(()).unwrap();
|
||||
smarm::sleep(Duration::from_millis(25));
|
||||
}
|
||||
*bq.lock().unwrap() = server.call(()).unwrap(); // count while traffic kept it quiet-free
|
||||
smarm::sleep(Duration::from_millis(140)); // now genuinely quiet
|
||||
drop(server);
|
||||
});
|
||||
assert_eq!(*before_quiet.lock().unwrap(), 0, "steady traffic must suppress idle");
|
||||
assert!(*idles.lock().unwrap() >= 1, "idle fires once the inbox falls quiet");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user