diff --git a/tokio-util/Cargo.toml b/tokio-util/Cargo.toml index 04971c6b1..8ad62b4c0 100644 --- a/tokio-util/Cargo.toml +++ b/tokio-util/Cargo.toml @@ -58,6 +58,9 @@ futures-test = "0.3.5" parking_lot = "0.12.0" tempfile = "3.1.0" +[target.'cfg(loom)'.dev-dependencies] +loom = { version = "0.5.2", features = ["futures", "checkpoint"] } + [package.metadata.docs.rs] all-features = true # enable unstable features in the documentation diff --git a/tokio-util/src/either.rs b/tokio-util/src/either.rs index 9225e53ca..421e930d5 100644 --- a/tokio-util/src/either.rs +++ b/tokio-util/src/either.rs @@ -164,7 +164,7 @@ where } } -#[cfg(test)] +#[cfg(all(test, not(loom)))] mod tests { use super::*; use tokio::io::{repeat, AsyncReadExt, Repeat}; diff --git a/tokio-util/src/loom.rs b/tokio-util/src/loom.rs index dd03feaba..9557e89a4 100644 --- a/tokio-util/src/loom.rs +++ b/tokio-util/src/loom.rs @@ -1 +1,5 @@ +#[cfg(not(loom))] pub(crate) use std::sync; + +#[cfg(loom)] +pub(crate) use loom::sync; diff --git a/tokio-util/src/sync/cancellation_token/tree_node.rs b/tokio-util/src/sync/cancellation_token/tree_node.rs index f9068bc09..a7c439a7b 100644 --- a/tokio-util/src/sync/cancellation_token/tree_node.rs +++ b/tokio-util/src/sync/cancellation_token/tree_node.rs @@ -298,12 +298,15 @@ pub(crate) fn cancel(node: &Arc) { if locked_node.is_cancelled { return; } + println!("a"); // One by one, adopt grandchildren and then cancel and detach the child while let Some(child) = locked_node.children.pop() { + println!("b1"); // This can't deadlock because the mutex we are already // holding is the parent of child. let mut locked_child = child.inner.lock().unwrap(); + println!("b2"); // Detach the child from node // No need to modify node.children, as the child already got removed with `.pop` @@ -356,10 +359,12 @@ pub(crate) fn cancel(node: &Arc) { // Now the child is cancelled and detached and all its children are adopted. // Just continue until all (including adopted) children are cancelled and detached. } + println!("c"); // Cancel the node itself. locked_node.is_cancelled = true; locked_node.children = Vec::new(); drop(locked_node); node.waker.notify_waiters(); + println!("d"); } diff --git a/tokio-util/src/sync/mod.rs b/tokio-util/src/sync/mod.rs index 1ae1b7ee4..0ce9e3486 100644 --- a/tokio-util/src/sync/mod.rs +++ b/tokio-util/src/sync/mod.rs @@ -13,3 +13,6 @@ pub use poll_semaphore::PollSemaphore; mod reusable_box; pub use reusable_box::ReusableBoxFuture; + +#[cfg(loom)] +mod tests; diff --git a/tokio-util/src/sync/tests/loom_cancellation_token.rs b/tokio-util/src/sync/tests/loom_cancellation_token.rs index b57503ddb..f15629c7f 100644 --- a/tokio-util/src/sync/tests/loom_cancellation_token.rs +++ b/tokio-util/src/sync/tests/loom_cancellation_token.rs @@ -48,6 +48,7 @@ fn cancel_token_owned() { #[test] fn cancel_with_child() { loom::model(|| { + println!("FOO"); let token = CancellationToken::new(); let token1 = token.clone(); let token2 = token.clone(); @@ -153,20 +154,27 @@ fn drop_and_cancel_token() { #[test] fn cancel_parent_and_child() { loom::model(|| { + println!("FOO"); let token1 = CancellationToken::new(); let token2 = token1.clone(); let child_token = token1.child_token(); let th1 = thread::spawn(move || { + println!("drop parent"); drop(token1); + println!("drop parent done"); }); let th2 = thread::spawn(move || { + println!("cancel parent"); token2.cancel(); + println!("cancel parent done"); }); let th3 = thread::spawn(move || { + println!("cancel child"); child_token.cancel(); + println!("cancel child done"); }); assert_ok!(th1.join()); diff --git a/tokio-util/src/sync/tests/mod.rs b/tokio-util/src/sync/tests/mod.rs index 8b1378917..2fb10a934 100644 --- a/tokio-util/src/sync/tests/mod.rs +++ b/tokio-util/src/sync/tests/mod.rs @@ -1 +1 @@ - +mod loom_cancellation_token;