Skip to content

Commit 092d768

Browse files
committed
fix hang in wait_all_exit
self.wait() without await returns a Future, so the notified() object is not created until we await on the returned future. That means notify_waiters() can be called before notified() is. This leads to notified() waiting forever because notify_waiters is called only once, when the last waiter is dropped. notify_waiters() and notified() form a happens-before relationship. There are two possible scenarios: 1. If notified() comes before notify_waiters() this means we can safely await on notified(). 2. If notified() comes after notify_waiters() this means that what happened before it is visible in the notified() thread. Waiting on notified() at this point will block but we can check for waiters count, which is guaranteed to be 0 because it was set before notify_waiters() call. Let's move notified() call before checking that the number of waiters is 0. Signed-off-by: Alexandru Matei <alexandru.matei@uipath.com>
1 parent 44b31f7 commit 092d768

File tree

1 file changed

+14
-13
lines changed

1 file changed

+14
-13
lines changed

src/asynchronous/shutdown.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -144,24 +144,25 @@ impl Notifier {
144144
/// Wait for all [`Waiter`]s to drop.
145145
pub async fn wait_all_exit(&self) -> Result<(), Elapsed> {
146146
//debug_assert!(self.shared.is_shutdown());
147-
if self.waiters() == 0 {
148-
return Ok(());
149-
}
150-
let wait = self.wait();
151-
if self.waiters() == 0 {
152-
return Ok(());
153-
}
154-
wait.await
155-
}
156-
157-
async fn wait(&self) -> Result<(), Elapsed> {
158147
if let Some(tm) = self.wait_time {
159-
timeout(tm, self.shared.notify_exit.notified()).await
148+
timeout(tm, self.wait()).await
160149
} else {
161-
self.shared.notify_exit.notified().await;
150+
self.wait().await;
162151
Ok(())
163152
}
164153
}
154+
155+
async fn wait(&self) {
156+
while self.waiters() > 0 {
157+
let notified = self.shared.notify_exit.notified();
158+
if self.waiters() == 0 {
159+
return;
160+
}
161+
notified.await;
162+
// Some waiters could have been created in the meantime
163+
// by calling `subscribe`, loop again
164+
}
165+
}
165166
}
166167

167168
impl Drop for Notifier {

0 commit comments

Comments
 (0)