Skip to content

Commit 0c7d03e

Browse files
authoredOct 23, 2023
fix(ffi): fix deadlock in hyper_executor::poll_next (#3370)
poll_next locks the driver, and also calls drain_queue while holding that lock. Since drain_queue locks the driver too, that results in a deadlock. To fix, unlock the driver before calling drain_queue. Closes #3369
1 parent 54c8670 commit 0c7d03e

File tree

1 file changed

+25
-17
lines changed

1 file changed

+25
-17
lines changed
 

‎src/ffi/task.rs

+25-17
Original file line numberDiff line numberDiff line change
@@ -126,27 +126,35 @@ impl hyper_executor {
126126
let mut cx = Context::from_waker(&waker);
127127

128128
loop {
129-
match Pin::new(&mut *self.driver.lock().unwrap()).poll_next(&mut cx) {
130-
Poll::Ready(val) => return val,
131-
Poll::Pending => {
132-
// Check if any of the pending tasks tried to spawn
133-
// some new tasks. If so, drain into the driver and loop.
134-
if self.drain_queue() {
135-
continue;
136-
}
137-
138-
// If the driver called `wake` while we were polling,
139-
// we should poll again immediately!
140-
if self.is_woken.0.swap(false, Ordering::SeqCst) {
141-
continue;
142-
}
143-
144-
return None;
145-
}
129+
{
130+
// Scope the lock on the driver to ensure it is dropped before
131+
// calling drain_queue below.
132+
let mut driver = self.driver.lock().unwrap();
133+
match Pin::new(&mut *driver).poll_next(&mut cx) {
134+
Poll::Ready(val) => return val,
135+
Poll::Pending => {}
136+
};
146137
}
138+
139+
// poll_next returned Pending.
140+
// Check if any of the pending tasks tried to spawn
141+
// some new tasks. If so, drain into the driver and loop.
142+
if self.drain_queue() {
143+
continue;
144+
}
145+
146+
// If the driver called `wake` while we were polling,
147+
// we should poll again immediately!
148+
if self.is_woken.0.swap(false, Ordering::SeqCst) {
149+
continue;
150+
}
151+
152+
return None;
147153
}
148154
}
149155

156+
/// drain_queue locks both self.spawn_queue and self.driver, so it requires
157+
/// that neither of them be locked already.
150158
fn drain_queue(&self) -> bool {
151159
let mut queue = self.spawn_queue.lock().unwrap();
152160
if queue.is_empty() {

0 commit comments

Comments
 (0)
Please sign in to comment.