Skip to content

Commit 1979615

Browse files
authored
process: fix panic from spurious pidfd wakeup (#7494)
1 parent f669a60 commit 1979615

File tree

3 files changed

+44
-20
lines changed

3 files changed

+44
-20
lines changed

tokio/src/io/poll_evented.rs

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ impl<E: Source> PollEvented<E> {
125125
}
126126

127127
/// Returns a reference to the registration.
128-
#[cfg(feature = "net")]
128+
#[cfg(any(feature = "net", all(feature = "process", target_os = "linux")))]
129129
pub(crate) fn registration(&self) -> &Registration {
130130
&self.registration
131131
}
@@ -138,14 +138,6 @@ impl<E: Source> PollEvented<E> {
138138
Ok(inner)
139139
}
140140

141-
#[cfg(all(feature = "process", target_os = "linux"))]
142-
pub(crate) fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
143-
self.registration
144-
.poll_read_ready(cx)
145-
.map_err(io::Error::from)
146-
.map_ok(|_| ())
147-
}
148-
149141
/// Re-register under new runtime with `interest`.
150142
#[cfg(all(feature = "process", target_os = "linux"))]
151143
pub(crate) fn reregister(&mut self, interest: Interest) -> io::Result<()> {

tokio/src/process/unix/pidfd_reaper.rs

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::{
1919
pin::Pin,
2020
process::ExitStatus,
2121
sync::atomic::{AtomicBool, Ordering::Relaxed},
22-
task::{ready, Context, Poll},
22+
task::{Context, Poll},
2323
};
2424

2525
#[derive(Debug)]
@@ -117,17 +117,21 @@ where
117117
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
118118
let this = Pin::into_inner(self);
119119

120-
match ready!(this.pidfd.poll_read_ready(cx)) {
121-
Err(err) if is_rt_shutdown_err(&err) => {
122-
this.pidfd.reregister(Interest::READABLE)?;
123-
ready!(this.pidfd.poll_read_ready(cx))?
120+
match this.pidfd.registration().poll_read_ready(cx) {
121+
Poll::Ready(Ok(evt)) => {
122+
if let Some(exit_code) = this.inner.try_wait()? {
123+
return Poll::Ready(Ok(exit_code));
124+
}
125+
this.pidfd.registration().clear_readiness(evt);
124126
}
125-
res => res?,
126-
}
127-
Poll::Ready(Ok(this
128-
.inner
129-
.try_wait()?
130-
.expect("pidfd is ready to read, the process should have exited")))
127+
Poll::Ready(Err(err)) if is_rt_shutdown_err(&err) => {}
128+
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
129+
Poll::Pending => return Poll::Pending,
130+
};
131+
132+
this.pidfd.reregister(Interest::READABLE)?;
133+
cx.waker().wake_by_ref();
134+
Poll::Pending
131135
}
132136
}
133137

tokio/tests/process_issue_7144.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#![cfg(feature = "process")]
2+
#![warn(rust_2018_idioms)]
3+
#![cfg(target_os = "linux")]
4+
#![cfg(not(miri))]
5+
6+
use tokio::process::Command;
7+
use tokio::time::{sleep, Duration};
8+
9+
#[tokio::test]
10+
async fn issue_7144() {
11+
let mut threads = vec![];
12+
for _ in 0..20 {
13+
threads.push(tokio::spawn(test_one()));
14+
}
15+
for thread in threads {
16+
thread.await.unwrap();
17+
}
18+
}
19+
20+
async fn test_one() {
21+
let mut t = Command::new("strace")
22+
.args("-o /dev/null -D sleep 5".split(' '))
23+
.spawn()
24+
.unwrap();
25+
sleep(Duration::from_millis(100)).await;
26+
unsafe { libc::kill(t.id().unwrap() as _, libc::SIGINT) };
27+
t.wait().await.unwrap();
28+
}

0 commit comments

Comments
 (0)