Skip to content

Commit 901f6d2

Browse files
authored
sync: drop wakers outside lock in semaphore (#5475)
1 parent a8fda87 commit 901f6d2

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

tokio/src/sync/batch_semaphore.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,7 @@ impl Semaphore {
444444
}
445445

446446
assert_eq!(acquired, 0);
447+
let mut old_waker = None;
447448

448449
// Otherwise, register the waker & enqueue the node.
449450
node.waker.with_mut(|waker| {
@@ -455,7 +456,7 @@ impl Semaphore {
455456
.map(|waker| !waker.will_wake(cx.waker()))
456457
.unwrap_or(true)
457458
{
458-
*waker = Some(cx.waker().clone());
459+
old_waker = std::mem::replace(waker, Some(cx.waker().clone()));
459460
}
460461
});
461462

@@ -468,6 +469,8 @@ impl Semaphore {
468469

469470
waiters.queue.push_front(node);
470471
}
472+
drop(waiters);
473+
drop(old_waker);
471474

472475
Pending
473476
}

tokio/src/sync/tests/semaphore_batch.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,3 +252,32 @@ fn cancel_acquire_releases_permits() {
252252
assert_eq!(6, s.available_permits());
253253
assert_ok!(s.try_acquire(6));
254254
}
255+
256+
#[test]
257+
fn release_permits_at_drop() {
258+
use crate::sync::semaphore::*;
259+
use futures::task::ArcWake;
260+
use std::future::Future;
261+
use std::sync::Arc;
262+
263+
let sem = Arc::new(Semaphore::new(1));
264+
265+
struct ReleaseOnDrop(Option<OwnedSemaphorePermit>);
266+
267+
impl ArcWake for ReleaseOnDrop {
268+
fn wake_by_ref(_arc_self: &Arc<Self>) {}
269+
}
270+
271+
let mut fut = Box::pin(async {
272+
let _permit = sem.acquire().await.unwrap();
273+
});
274+
275+
// Second iteration shouldn't deadlock.
276+
for _ in 0..=1 {
277+
let waker = futures::task::waker(Arc::new(ReleaseOnDrop(
278+
sem.clone().try_acquire_owned().ok(),
279+
)));
280+
let mut cx = std::task::Context::from_waker(&waker);
281+
assert!(fut.as_mut().poll(&mut cx).is_pending());
282+
}
283+
}

0 commit comments

Comments
 (0)