diff --git a/src/enabled/task.rs b/src/enabled/task.rs index 017e19c..d5370e0 100644 --- a/src/enabled/task.rs +++ b/src/enabled/task.rs @@ -42,6 +42,15 @@ pin_project! { }, Done, } + + impl<'a, F> PinnedDrop for ParcheckTaskFuture<'a, F> { + fn drop(this: Pin<&mut Self>) { + let proj = this.project(); + if let ParcheckTaskFutureProj::Controlled { task, .. } = proj { + task.send_event(TaskEvent::TaskFinished); + } + } + } } #[cfg(feature = "tracing")] diff --git a/tests/examples/basic.rs b/tests/examples/basic.rs index 6d245ed..54d45cb 100644 --- a/tests/examples/basic.rs +++ b/tests/examples/basic.rs @@ -2,8 +2,10 @@ use std::collections::HashMap; use std::future; use std::mem::size_of_val; use std::sync::Mutex; +use std::time::Duration; use parcheck::Trace; +use tokio::sync::oneshot; struct Observer { trace: Mutex, @@ -136,7 +138,7 @@ async fn does_not_double_panic() { #[tokio::test] #[should_panic( - expected = "operation 'outer' already in progress for task 'reentrant' (operation at tests/examples/basic.rs:146)" + expected = "operation 'outer' already in progress for task 'reentrant' (operation at tests/examples/basic.rs:148)" )] async fn detects_reentrant_task() { parcheck::runner() @@ -219,3 +221,32 @@ async fn futures_arent_too_large() { "task future size: {task_size} -> {wrapped_task_size}" ); } + +#[tokio::test] +async fn handles_cancellation() { + tokio::time::timeout(Duration::from_millis(100), async { + parcheck::runner() + .run(["dropped_task"], || async move { + let (tx, rx) = oneshot::channel(); + let task = parcheck::task!("dropped_task", { + async { + parcheck::operation!("op", { + async { + tx.send(()).unwrap(); + future::pending::<()>().await; + } + }) + .await; + } + }); + + tokio::select! { + _ = task => unreachable!(), + _ = rx => {}, + } + }) + .await; + }) + .await + .unwrap(); +}