Repository: tokio-rs/tokio
File: tokio/src/process/unix/pidfd_reaper.rs (Rust)

Code before:
                .expect("inner has gone away"),
        )
        .poll(cx)
    }
}

impl<W> Kill for PidfdReaper<W>
where
    W: Wait + Send + Sync + Unpin + Kill + 'static,
{
    fn kill(&mut self) -> io::Result<()> {
        self.inner_mut().kill()
    }
}

impl<W> Drop for PidfdReaper<W>
where
    W: Wait + Send + Sync + Unpin + 'static,
{
    fn drop(&mut self) {
        let mut reaper_inner = self.0.take().expect("inner has gone away");
        if let Ok(Some(_)) = reaper_inner.inner.try_wait() {
            return;
        }

        Handle {
            inner: reaper_inner.pidfd.scheduler_handle().clone(),
        }
        .spawn(async move {
            let _ = reaper_inner.await;
        });
    }
}

#[cfg(all(test, not(loom), not(miri)))]
mod test {
    use super::*;
    use crate::runtime::{Builder as RuntimeBuilder, Runtime};
    use std::process::{Command, Output};

    fn create_runtime() -> Runtime {
        RuntimeBuilder::new_current_thread()
            .enable_io()
            .build()
            .unwrap()
    }

    fn run_test(fut: impl Future<Output = ()>) {
        create_runtime().block_on(fut)
    }


Review comment (refactor):
My advice is to avoid doing any kind of spawn or trying to shim async code during drop. Instead I'd suggest exploring building this kind of clean up into the runtime driver itself (e.g. add a new `process`-specific driver the way we have a `signal` specific one) which would avoid potential headaches such as:

* The `drop` being run outside of a Tokio runtime context (this should probably never happen, but someone might be doing something weird like deferred drop of cancelled futures in a dedicated thread)
* The runtime being present but not actually turning (e.g. sometimes people use dedicated runtimes, that runtime might be blocked intentionally or otherwise not actually executing in which case clean up tasks might pile up without being polled)

Code after:
impl<W, Q> PidfdReaper<W, Q>
where
    W: Wait + Unpin,
    Q: OrphanQueue<W> + Unpin,
{
    pub(crate) fn new(inner: W, orphan_queue: Q) -> Result<Self, (Option<io::Error>, W)> {
        if let Some(pidfd) = Pidfd::open(inner.id()) {
            match PollEvented::new_with_interest(pidfd, Interest::READABLE) {
                Ok(pidfd) => Ok(Self {
                    inner: Some(PidfdReaperInner { pidfd, inner }),
                    orphan_queue,
                }),
                Err(io_error) => Err((Some(io_error), inner)),
            }
        } else {
            Err((None, inner))
        }
    }

    pub(crate) fn inner_mut(&mut self) -> &mut W {
        &mut self.inner.as_mut().expect("inner has gone away").inner
    }
}

impl<W, Q> Future for PidfdReaper<W, Q>
where
    W: Wait + Unpin,
    Q: OrphanQueue<W> + Unpin,
{
    type Output = io::Result<ExitStatus>;

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        Pin::new(
            Pin::into_inner(self)
                .inner
                .as_mut()
                .expect("inner has gone away"),
        )
        .poll(cx)
    }
}

impl<W, Q> Kill for PidfdReaper<W, Q>
where
    W: Wait + Unpin + Kill,
    Q: OrphanQueue<W> + Unpin,
{
    fn kill(&mut self) -> io::Result<()> {
        self.inner_mut().kill()
    }
}