meta lock waiters keep their queue place; deadlines cover pin_agent git and kill the whole process group
This commit is contained in:
parent
7cde4e7b15
commit
161dd8c44b
4 changed files with 138 additions and 51 deletions
|
|
@ -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]
|
||||
|
|
|
|||
Loading…
Reference in a new issue