diff --git a/Cargo.lock b/Cargo.lock index 8ff0b11b0e..32960a60c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3006,6 +3006,7 @@ dependencies = [ "reqwest", "rustls", "rustls-native-certs", + "rusty-fork", "serde", "static_assertions", "tempfile", diff --git a/libdd-common/Cargo.toml b/libdd-common/Cargo.toml index ba3e5c9378..aef95db03c 100644 --- a/libdd-common/Cargo.toml +++ b/libdd-common/Cargo.toml @@ -84,6 +84,7 @@ multer = "3.1" bytes = "1.11.1" rand = "0.8" tempfile = "3.8" +rusty-fork = "0.3" tokio = { version = "1.23", features = ["rt", "macros", "time"] } [features] diff --git a/libdd-common/src/test_utils.rs b/libdd-common/src/test_utils.rs index 01c1c208ee..ca5251084e 100644 --- a/libdd-common/src/test_utils.rs +++ b/libdd-common/src/test_utils.rs @@ -419,6 +419,7 @@ async fn parse_multipart(boundary: String, body: Vec) -> anyhow::Result= 1, - "Expected at least 1 thread, got {}", - initial_count - ); - // Spawn some threads and verify the count increases - use std::sync::{Arc, Barrier}; - let barrier = Arc::new(Barrier::new(6)); // 5 spawned threads + main thread - - let handles: Vec<_> = (0..5) - .map(|_| { - let barrier = Arc::clone(&barrier); - std::thread::spawn(move || { - barrier.wait(); - std::thread::sleep(std::time::Duration::from_millis(50)); + // This test must run in its own process to get accurate thread counts, + // since the test runner and other parallel tests spawn threads. + rusty_fork_test! { + #[test] + #[cfg_attr(miri, ignore)] + fn test_count_active_threads() { + use crate::test_utils::count_active_threads; + + let initial_count = count_active_threads().expect("Failed to count threads"); + assert!( + initial_count >= 1, + "Expected at least 1 thread, got {}", + initial_count + ); + + // Spawn some threads and verify the count increases + use std::sync::{Arc, Barrier}; + let barrier = Arc::new(Barrier::new(6)); // 5 spawned threads + main thread + + let handles: Vec<_> = (0..5) + .map(|_| { + let barrier = Arc::clone(&barrier); + std::thread::spawn(move || { + barrier.wait(); + std::thread::sleep(std::time::Duration::from_millis(50)); + }) }) - }) - .collect(); - - barrier.wait(); - let count_with_threads = count_active_threads().expect("Failed to count threads"); - assert!( - count_with_threads >= initial_count + 5, - "Expected at least {} threads (initial: {}, with 5 spawned: {})", - initial_count + 5, - initial_count, - count_with_threads - ); + .collect(); + + barrier.wait(); + let count_with_threads = count_active_threads().expect("Failed to count threads"); + assert!( + count_with_threads >= initial_count + 5, + "Expected at least {} threads (initial: {}, with 5 spawned: {})", + initial_count + 5, + initial_count, + count_with_threads + ); + + for handle in handles { + handle.join().expect("Thread should join successfully"); + } - for handle in handles { - handle.join().expect("Thread should join successfully"); + let count_after_join = count_active_threads().expect("Failed to count threads"); + // Allow up to 1 extra: some platforms (e.g. CentOS 7) lazily spawn a helper thread + assert!( + count_after_join <= initial_count + 1, + "Expected thread count to return to {} or {} after join, got {}", + initial_count, + initial_count + 1, + count_after_join + ); } - - let count_after_join = count_active_threads().expect("Failed to count threads"); - // Allow up to 1 extra: some platforms (e.g. CentOS 7) lazily spawn a helper thread - assert!( - count_after_join <= initial_count + 1, - "Expected thread count to return to {} or {} after join, got {}", - initial_count, - initial_count + 1, - count_after_join - ); } }