Skip to content

Commit

Permalink
Use PidfdReaper in unix::Child if possible
Browse files Browse the repository at this point in the history
fallback to signal-based `Reaper` if pidfd cannot be created.

Signed-off-by: Jiahao XU <[email protected]>
  • Loading branch information
NobodyXu committed Nov 15, 2023
1 parent 706cc1f commit 50518d0
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 19 deletions.
54 changes: 41 additions & 13 deletions tokio/src/process/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,15 @@ impl OrphanQueue<StdChild> for GlobalOrphanQueue {
}

#[must_use = "futures do nothing unless polled"]
pub(crate) struct Child {
inner: Reaper<StdChild, GlobalOrphanQueue, Signal>,
pub(crate) enum Child {
SignalReaper(Reaper<StdChild, GlobalOrphanQueue, Signal>),
#[cfg(target_os = "linux")]
PidfdReaper(pidfd_reaper::PidfdReaper<StdChild>),
}

impl fmt::Debug for Child {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("Child")
.field("pid", &self.inner.id())
.finish()
fmt.debug_struct("Child").field("pid", &self.id()).finish()
}
}

Expand All @@ -121,12 +121,24 @@ pub(crate) fn spawn_child(cmd: &mut std::process::Command) -> io::Result<Spawned
let stdout = child.stdout.take().map(stdio).transpose()?;
let stderr = child.stderr.take().map(stdio).transpose()?;

#[cfg(target_os = "linux")]
match pidfd_reaper::PidfdReaper::new(child) {
Ok(pidfd_reaper) => {
return Ok(SpawnedChild {
child: Child::PidfdReaper(pidfd_reaper),
stdin,
stdout,
stderr,
})
}
Err((Some(err), _child)) => return Err(err),
Err((None, child_returned)) => child = child_returned,
}

let signal = signal(SignalKind::child())?;

Ok(SpawnedChild {
child: Child {
inner: Reaper::new(child, GlobalOrphanQueue, signal),
},
child: Child::SignalReaper(Reaper::new(child, GlobalOrphanQueue, signal)),
stdin,
stdout,
stderr,
Expand All @@ -135,25 +147,41 @@ pub(crate) fn spawn_child(cmd: &mut std::process::Command) -> io::Result<Spawned

impl Child {
pub(crate) fn id(&self) -> u32 {
self.inner.id()
match self {
Self::SignalReaper(signal_reaper) => signal_reaper.id(),
#[cfg(target_os = "linux")]
Self::PidfdReaper(pidfd_reaper) => pidfd_reaper.id(),
}
}

fn std_child(&mut self) -> &mut StdChild {
match self {
Self::SignalReaper(signal_reaper) => signal_reaper.inner_mut(),
#[cfg(target_os = "linux")]
Self::PidfdReaper(pidfd_reaper) => pidfd_reaper.inner_mut(),
}
}

pub(crate) fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
self.inner.inner_mut().try_wait()
self.std_child().try_wait()
}
}

impl Kill for Child {
fn kill(&mut self) -> io::Result<()> {
self.inner.kill()
self.std_child().kill()
}
}

impl Future for Child {
type Output = io::Result<ExitStatus>;

fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
Pin::new(&mut self.inner).poll(cx)
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match Pin::into_inner(self) {
Self::SignalReaper(signal_reaper) => Pin::new(signal_reaper).poll(cx),
#[cfg(target_os = "linux")]
Self::PidfdReaper(pidfd_reaper) => Pin::new(pidfd_reaper).poll(cx),
}
}
}

Expand Down
12 changes: 6 additions & 6 deletions tokio/src/process/unix/pidfd_reaper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,14 +112,14 @@ impl<W> PidfdReaper<W>
where
W: Wait + Send + Sync + Unpin + 'static,
{
pub(crate) fn new(inner: W) -> io::Result<Option<Self>> {
pub(crate) fn new(inner: W) -> Result<Self, (Option<io::Error>, W)> {
if let Some(pidfd) = Pidfd::open(inner.id()) {
Ok(Some(Self(Some(PidfdReaperInner {
pidfd: PollEvented::new_with_interest(pidfd, Interest::READABLE)?,
inner,
}))))
match PollEvented::new_with_interest(pidfd, Interest::READABLE) {
Ok(pidfd) => Ok(Self(Some(PidfdReaperInner { pidfd, inner }))),
Err(io_error) => Err((Some(io_error), inner)),
}
} else {
Ok(None)
Err((None, inner))
}
}

Expand Down

0 comments on commit 50518d0

Please sign in to comment.