Watch
0
0
Fork
You've already forked hyperhive
0

meta lock waiters keep their queue place; deadlines cover pin_agent git and kill the whole process group

This commit is contained in:
atlas 2026-10-20 00:23:39 +02:00 • committed by mara
commit 161dd8c44b
4 changed files with 138 additions and 51 deletions

View file

@ -81,10 +81,14 @@ async fn lock_notifying<'a>(
mut on_stall: impl FnMut(&'static str, Duration),
) -> MetaGuard<'a> {
let start = Instant::now();
// One acquire future for the whole wait: dropping it loses the place in
// the mutex's FIFO queue, so the stall timer runs beside it, not around it.
let acquire = lock.lock();
tokio::pin!(acquire);
let guard = loop {
match tokio::time::timeout(interval, lock.lock()).await {
Ok(guard) => break guard,
Err(_) => on_stall(
tokio::select! {
guard = &mut acquire => break guard,
() = tokio::time::sleep(interval) => on_stall(
*holder.lock().unwrap_or_else(PoisonError::into_inner),
start.elapsed(),
),
@ -2202,6 +2206,39 @@ mod lock_wait_tests {
drop(second);
assert_eq!(*stalls.borrow(), vec![("first", 60), ("first", 120)]);
}
/// Waiters get the lock in arrival order even when one of them stalls past
/// an interval: A queues at 0s, B at 30s, the holder releases at 70s. A
/// re-armed acquire would put A (stalled at 60s) behind B.
#[tokio::test(start_paused = true)]
async fn waiters_acquire_in_arrival_order_across_a_stall() {
use std::sync::Arc;
static LOCK: Mutex<()> = Mutex::const_new(());
static HOLDER: StdMutex<&'static str> = StdMutex::new("");
let order = Arc::new(StdMutex::new(Vec::new()));
let first = lock_notifying(&LOCK, &HOLDER, "first", Duration::from_mins(1), |_, _| {
panic!("an uncontended lock must not report a stall")
})
.await;
let spawn_waiter = |op: &'static str| {
let order = Arc::clone(&order);
tokio::spawn(async move {
let guard =
lock_notifying(&LOCK, &HOLDER, op, Duration::from_mins(1), |_, _| {}).await;
order.lock().unwrap().push(op);
drop(guard);
})
};
let a = spawn_waiter("A");
tokio::time::sleep(Duration::from_secs(30)).await;
let b = spawn_waiter("B");
tokio::time::sleep(Duration::from_secs(40)).await;
drop(first);
a.await.expect("A");
b.await.expect("B");
assert_eq!(*order.lock().unwrap(), vec!["A", "B"]);
}
/// A hung nix child comes back as a timeout error naming the command. The
/// deadline and program are injected; the production const is untouched.
#[tokio::test]