Skip to content

Commit

Permalink
sync: drop wakers outside lock in semaphore (tokio-rs#5475)
Browse files Browse the repository at this point in the history
  • Loading branch information
amab8901 committed Feb 27, 2023
1 parent be110cc commit 480f2de
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 1 deletion.
5 changes: 4 additions & 1 deletion tokio/src/sync/batch_semaphore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,7 @@ impl Semaphore {
}

assert_eq!(acquired, 0);
let mut old_waker = None;

// Otherwise, register the waker & enqueue the node.
node.waker.with_mut(|waker| {
Expand All @@ -455,7 +456,7 @@ impl Semaphore {
.map(|waker| !waker.will_wake(cx.waker()))
.unwrap_or(true)
{
*waker = Some(cx.waker().clone());
old_waker = std::mem::replace(waker, Some(cx.waker().clone()));
}
});

Expand All @@ -468,6 +469,8 @@ impl Semaphore {

waiters.queue.push_front(node);
}
drop(waiters);
drop(old_waker);

Pending
}
Expand Down
29 changes: 29 additions & 0 deletions tokio/src/sync/tests/semaphore_batch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,32 @@ fn cancel_acquire_releases_permits() {
assert_eq!(6, s.available_permits());
assert_ok!(s.try_acquire(6));
}

#[test]
fn release_permits_at_drop() {
use crate::sync::semaphore::*;
use futures::task::ArcWake;
use std::future::Future;
use std::sync::Arc;

let sem = Arc::new(Semaphore::new(1));

struct ReleaseOnDrop(Option<OwnedSemaphorePermit>);

impl ArcWake for ReleaseOnDrop {
fn wake_by_ref(_arc_self: &Arc<Self>) {}
}

let mut fut = Box::pin(async {
let _permit = sem.acquire().await.unwrap();
});

// Second iteration shouldn't deadlock.
for _ in 0..=1 {
let waker = futures::task::waker(Arc::new(ReleaseOnDrop(
sem.clone().try_acquire_owned().ok(),
)));
let mut cx = std::task::Context::from_waker(&waker);
assert!(fut.as_mut().poll(&mut cx).is_pending());
}
}

0 comments on commit 480f2de

Please sign in to comment.