MCPcopy Create free account
hub / github.com/ScriptedAlchemy/tracedecay / debounce_loop

Function debounce_loop

src/daemon/git_watch.rs:501–561  ·  view source on GitHub ↗

The debounce state machine for a healthy watcher. Wakes on events, sleeps until the quiet deadline or the hard cap (whichever comes first), then drains and syncs. No busy polling.

(inner: &Arc<GitWatcherInner>, state: &Arc<WatchState>, common: &Path)

Source from the content-addressed store, hash-verified

499/// until the quiet deadline or the hard cap (whichever comes first), then
500/// drains and syncs. No busy polling.
501async fn debounce_loop(inner: &Arc<GitWatcherInner>, state: &Arc<WatchState>, common: &Path) {
502 let quiet = Duration::from_millis(inner.config.watch_debounce_ms);
503 let max_delay = Duration::from_millis(inner.config.watch_max_delay_ms);
504
505 #[cfg(test)]
506 state.entered_debounce.notify_one();
507
508 loop {
509 // Sleep until the first event arrives.
510 state.wake.notified().await;
511 state.health.beat();
512
513 // Coalesce: keep extending the quiet window until it settles or we hit
514 // the hard cap. If a rebase/merge is mid-flight, HOLD (keep waiting)
515 // until the markers disappear so we sync exactly once, after.
516 loop {
517 let (first, last) = {
518 let dirty = state.dirty.lock().await;
519 (dirty.first_event, dirty.last_event)
520 };
521 let now = Instant::now();
522 let quiet_deadline = last.map(|l| l + quiet);
523 let hard_deadline = first.map(|f| f + max_delay);
524
525 // If an operation is in flight, do not fire yet — wait for the next
526 // event (marker removal wakes us) or a short recheck tick.
527 if operation_in_flight(common) {
528 tokio::select! {
529 () = state.wake.notified() => { state.health.beat(); continue; }
530 () = tokio::time::sleep(Duration::from_secs(1)) => { continue; }
531 }
532 }
533
534 // Fire when the quiet window elapsed, but never later than the cap.
535 let fire_at = match (quiet_deadline, hard_deadline) {
536 (Some(q), Some(h)) => q.min(h),
537 (Some(q), None) => q,
538 (None, Some(h)) => h,
539 (None, None) => break, // nothing pending; back to outer wait
540 };
541 if now >= fire_at {
542 break;
543 }
544 let sleep_for = fire_at - now;
545 tokio::select! {
546 () = state.wake.notified() => { state.health.beat(); }
547 () = tokio::time::sleep(sleep_for) => {}
548 }
549 }
550
551 // Drain and execute exactly one coalesced sync pass.
552 let plan = {
553 let mut dirty = state.dirty.lock().await;
554 dirty.take()
555 };
556 if !plan.is_empty() {
557 execute_plan(inner, state, common, plan).await;
558 }

Callers 1

project_taskFunction · 0.85

Calls 5

operation_in_flightFunction · 0.85
execute_planFunction · 0.85
beatMethod · 0.80
takeMethod · 0.45
is_emptyMethod · 0.45

Tested by

no test coverage detected