//! Race condition tests for the announcement crate. //! Critical TOCTOU (Time-of-check to time-of-use) tests. //! Each test runs many iterations to catch timing-sensitive bugs. use announcement::Announcement; use std::sync::{ atomic::{AtomicBool, AtomicUsize, Ordering}, Arc, Barrier, }; use std::thread; use std::time::Duration; /// Test: Race condition test: race announce between check and setup /// /// Verifies: Verifies no race condition in race announce between check and setup /// Category: race #[test] fn test_race_announce_between_check_and_setup() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let announcement_clone = announcement.clone(); // Thread that announces at a random time let announcer_thread = thread::spawn(move || { // Try to hit the race window if iteration % 2 == 0 { thread::yield_now(); } announcer.announce(iteration).unwrap(); }); // Thread that checks then sets up listener let checker_thread = thread::spawn(move || { // Check let was_announced = announcement_clone.is_announced(); // Small window for race if iteration % 3 == 0 { thread::yield_now(); } // Setup listener let listener = announcement_clone.listener(); // Try to receive let value = listener.try_listen(); (was_announced, value) }); announcer_thread.join().unwrap(); let (was_announced, value) = checker_thread.join().unwrap(); // If it was announced when we checked, we must receive the value if was_announced { assert_eq!(value, Some(iteration), "Failed at iteration {}", iteration); } // If not announced when checked, we might or might not have value // (depends on exact timing) } } /// Test: Race condition test: race announce during listen setup /// /// Verifies: Verifies no race condition in race announce during listen setup /// Category: race #[test] fn test_race_announce_during_listen_setup() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); // Thread that tries to announce during async listen setup thread::spawn(move || { // Varying delays to hit different points match iteration % 5 { 0 => {} 1 => thread::yield_now(), 2 => thread::sleep(Duration::from_nanos(1)), 3 => thread::sleep(Duration::from_nanos(10)), _ => thread::sleep(Duration::from_nanos(100)), } announcer.announce(iteration).unwrap(); }); // The async listen would handle this in async context // For this test, we use try_listen with retries let start = std::time::Instant::now(); loop { if let Some(value) = listener.try_listen() { assert_eq!(value, iteration); break; } if start.elapsed() > Duration::from_millis(100) { panic!("Timeout at iteration {}", iteration); } thread::yield_now(); } } } /// Test: Concurrency: race concurrent announce and listen /// /// Verifies: Verifies race concurrent announce and listen works correctly with concurrent access /// Category: race #[test] fn test_race_concurrent_announce_and_listen() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let barrier = Arc::new(Barrier::new(2)); let announcement_clone = announcement.clone(); let barrier1 = barrier.clone(); let listen_thread = thread::spawn(move || { let listener = announcement_clone.listener(); // Synchronize with announce thread barrier1.wait(); // Immediately try to listen listener.try_listen() }); let barrier2 = barrier.clone(); let announce_thread = thread::spawn(move || { // Synchronize with listen thread barrier2.wait(); // Immediately announce announcer.announce(iteration).unwrap(); }); announce_thread.join().unwrap(); let result = listen_thread.join().unwrap(); // The listener might or might not see the value depending on exact timing if let Some(value) = result { assert_eq!(value, iteration); } } } /// Test: Race condition test: race check then act /// /// Verifies: Verifies no race condition in race check then act /// Category: race #[test] fn test_race_check_then_act() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let ann1 = announcement.clone(); let ann2 = announcement.clone(); let racer1 = thread::spawn(move || { // Check if !ann1.is_announced() { // Race window here thread::yield_now(); // Act based on check let listener = ann1.listener(); listener.try_listen() } else { let listener = ann1.listener(); listener.try_listen() } }); let racer2 = thread::spawn(move || { // Check if !ann2.is_announced() { // Different timing if iteration % 2 == 0 { thread::yield_now(); } let listener = ann2.listener(); listener.try_listen() } else { let listener = ann2.listener(); listener.try_listen() } }); // Announcer with varying timing thread::spawn(move || match iteration % 4 { 0 => announcer.announce(iteration).unwrap(), 1 => { thread::yield_now(); announcer.announce(iteration).unwrap(); } 2 => { thread::sleep(Duration::from_nanos(1)); announcer.announce(iteration).unwrap(); } _ => { thread::sleep(Duration::from_nanos(10)); announcer.announce(iteration).unwrap(); } }); let r1 = racer1.join().unwrap(); let r2 = racer2.join().unwrap(); // Both should see the same value if they see anything if r1.is_some() && r2.is_some() { assert_eq!(r1, r2); assert_eq!(r1, Some(iteration)); } } } /// Test: Race condition test: race listener creation at announcement /// /// Verifies: Verifies no race condition in race listener creation at announcement /// Category: race #[test] fn test_race_listener_creation_at_announcement() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let barrier = Arc::new(Barrier::new(11)); // 10 threads + announcer let mut handles = vec![]; for i in 0..10 { let ann = announcement.clone(); let barrier = barrier.clone(); handles.push(thread::spawn(move || { // All threads wait barrier.wait(); // Create listener at same moment as announcement let listener = ann.listener(); (i, listener.try_listen()) })); } let barrier_announce = barrier.clone(); let announce_handle = thread::spawn(move || { barrier_announce.wait(); announcer.announce(iteration).unwrap(); }); announce_handle.join().unwrap(); for handle in handles { let (thread_id, result) = handle.join().unwrap(); // Some might see it, some might not, but if they do, value must be correct if let Some(value) = result { assert_eq!( value, iteration, "Thread {} got wrong value at iteration {}", thread_id, iteration ); } } } } /// Test: Race condition test: race multiple checks with announce /// /// Verifies: Verifies no race condition in race multiple checks with announce /// Category: race #[test] fn test_race_multiple_checks_with_announce() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let ann1 = announcement.clone(); let ann2 = announcement.clone(); let checker1 = thread::spawn(move || { let mut results = vec![]; for _ in 0..10 { results.push(ann1.is_announced()); if iteration % 100 == 0 { thread::yield_now(); } } results }); let checker2 = thread::spawn(move || { let mut results = vec![]; for _ in 0..10 { results.push(ann2.is_announced()); if iteration % 100 == 50 { thread::yield_now(); } } results }); // Announce somewhere in the middle thread::spawn(move || { thread::sleep(Duration::from_nanos(iteration as u64 % 100)); announcer.announce(iteration).unwrap(); }); let results1 = checker1.join().unwrap(); let results2 = checker2.join().unwrap(); // Verify monotonicity - once true, always true let mut seen_true1 = false; for &result in &results1 { if seen_true1 { assert!( result, "is_announced went from true to false at iteration {}", iteration ); } if result { seen_true1 = true; } } let mut seen_true2 = false; for &result in &results2 { if seen_true2 { assert!( result, "is_announced went from true to false at iteration {}", iteration ); } if result { seen_true2 = true; } } } } /// Test: Race condition test: race arc operations /// /// Verifies: Verifies no race condition in race arc operations /// Category: race #[test] fn test_race_arc_operations() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let data = Arc::new(iteration); let data_clone = data.clone(); let listener = announcement.listener(); let arc_thread = thread::spawn(move || { // Manipulate Arc ref counts during announcement let mut clones = vec![]; for _ in 0..10 { clones.push(data_clone.clone()); if iteration % 10 == 5 { thread::yield_now(); } } Arc::strong_count(&clones[0]) }); let announce_thread = thread::spawn(move || { thread::sleep(Duration::from_nanos(iteration as u64 % 50)); announcer.announce(data).unwrap(); }); let _count = arc_thread.join().unwrap(); announce_thread.join().unwrap(); // Verify value is correct if let Some(received) = listener.try_listen() { assert_eq!(*received, iteration); // Count should be reasonable assert!(Arc::strong_count(&received) > 0); } } } /// Test: Race condition test: race drop timing /// /// Verifies: Verifies no race condition in race drop timing /// Category: race #[test] fn test_race_drop_timing() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let announcement_clone = announcement.clone(); let dropper = thread::spawn(move || { let listener = announcement_clone.listener(); // Hold listener briefly then drop match iteration % 4 { 0 => drop(listener), 1 => { thread::yield_now(); drop(listener); } 2 => { thread::sleep(Duration::from_nanos(1)); drop(listener); } _ => { let _ = listener.try_listen(); drop(listener); } } }); // Try to announce during the drop let announce_result = thread::spawn(move || { thread::sleep(Duration::from_nanos(iteration as u64 % 10)); announcer.announce(iteration) }); dropper.join().unwrap(); announce_result.join().unwrap().unwrap(); // Create new listener after the race let final_listener = announcement.listener(); assert_eq!(final_listener.try_listen(), Some(iteration)); } } /// Test: Race condition test: race memory barriers /// /// Verifies: Verifies no race condition in race memory barriers /// Category: race #[test] fn test_race_memory_barriers() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let flag = Arc::new(AtomicBool::new(false)); let value = Arc::new(AtomicUsize::new(0)); let flag1 = flag.clone(); let value1 = value.clone(); let listener = announcement.listener(); let reader = thread::spawn(move || { // Spin until flag is set while !flag1.load(Ordering::Acquire) { std::hint::spin_loop(); } // Flag is set, value should be visible let v = value1.load(Ordering::Relaxed); // Also check announcement let announced = listener.try_listen(); (v, announced) }); let flag2 = flag.clone(); let value2 = value.clone(); let writer = thread::spawn(move || { // Set value value2.store(iteration, Ordering::Relaxed); // Announce announcer.announce(iteration).unwrap(); // Set flag (with release to ensure value is visible) flag2.store(true, Ordering::Release); }); writer.join().unwrap(); let (seen_value, announced_value) = reader.join().unwrap(); // Both should see the iteration value assert_eq!(seen_value, iteration); assert_eq!(announced_value, Some(iteration)); } } /// Test: Race condition test: race listener chain /// /// Verifies: Verifies no race condition in race listener chain /// Category: race #[test] fn test_race_listener_chain() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let mut handles = vec![]; // Create chain of threads, each creating a listener from previous let first_listener = Arc::new(announcement.listener()); for i in 0..5 { let listener = first_listener.clone(); handles.push(thread::spawn(move || { let my_listener = (*listener).clone(); // Varying delays match (iteration + i) % 3 { 0 => {} 1 => thread::yield_now(), _ => thread::sleep(Duration::from_nanos(1)), } my_listener.try_listen() })); } // Announce with timing variety thread::spawn(move || { thread::sleep(Duration::from_nanos((iteration % 10) as u64)); announcer.announce(iteration).unwrap(); }); // All should eventually see the same value let mut results = vec![]; for handle in handles { results.push(handle.join().unwrap()); } // Filter out None values let non_none: Vec<_> = results.iter().filter_map(|&x| x).collect(); if !non_none.is_empty() { // All non-None should be the same for &value in &non_none { assert_eq!(value, iteration); } } } } #[cfg(feature = "std")] /// Test: Race condition test: race spurious wakeup /// /// Verifies: Verifies no race condition in race spurious wakeup /// Category: race #[test] fn test_race_spurious_wakeup() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let listener = announcement.listener(); // Thread that might cause spurious activity let noise = thread::spawn(move || { for _ in 0..10 { // Create and drop listeners rapidly let _ = announcement.listener(); if iteration % 100 == 50 { thread::yield_now(); } } }); // Thread that announces let announce = thread::spawn(move || { thread::sleep(Duration::from_nanos((iteration % 20) as u64)); announcer.announce(iteration).unwrap(); }); noise.join().unwrap(); announce.join().unwrap(); // Verify correct value despite noise assert_eq!(listener.try_listen(), Some(iteration)); } } /// Test: Race condition test: race visibility /// /// Verifies: Verifies no race condition in race visibility /// Category: race #[test] fn test_race_visibility() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let barrier = Arc::new(Barrier::new(6)); let mut handles = vec![]; // Multiple threads checking visibility for i in 0..5 { let ann = announcement.clone(); let barrier = barrier.clone(); handles.push(thread::spawn(move || { let listener = ann.listener(); barrier.wait(); // Check multiple times quickly let mut results = vec![]; for _ in 0..10 { results.push(listener.try_listen()); } (i, results) })); } let barrier_announce = barrier.clone(); let announce_handle = thread::spawn(move || { barrier_announce.wait(); // Announce right after barrier announcer.announce(iteration).unwrap(); }); announce_handle.join().unwrap(); for handle in handles { let (thread_id, results) = handle.join().unwrap(); // Once a thread sees the value, all subsequent checks should see it let mut seen = false; for result in results { if seen { assert_eq!( result, Some(iteration), "Thread {} lost visibility at iteration {}", thread_id, iteration ); } if result.is_some() { seen = true; assert_eq!(result, Some(iteration)); } } } } } /// Test: Race condition test: race load store /// /// Verifies: Verifies no race condition in race load store /// Category: race #[test] fn test_race_load_store() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let checking = Arc::new(AtomicBool::new(true)); let checking_clone = checking.clone(); let ann = announcement.clone(); let checker = thread::spawn(move || { let mut transition_point = None; let mut check_count = 0; while checking_clone.load(Ordering::Acquire) { check_count += 1; if ann.is_announced() && transition_point.is_none() { transition_point = Some(check_count); } } (transition_point, check_count) }); // Announce with varying timing thread::spawn(move || { thread::sleep(Duration::from_nanos((iteration % 100) as u64)); announcer.announce(iteration).unwrap(); }); thread::sleep(Duration::from_nanos(200)); checking.store(false, Ordering::Release); let (transition, total_checks) = checker.join().unwrap(); // If we saw a transition, it should be reasonable if let Some(point) = transition { assert!(point > 0 && point <= total_checks); } } } /// Test: Race condition test: race cache coherency /// /// Verifies: Verifies no race condition in race cache coherency /// Category: race #[test] fn test_race_cache_coherency() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); // Multiple CPUs accessing the same announcement let mut handles = vec![]; for i in 0..8 { let ann = announcement.clone(); handles.push(thread::spawn(move || { let listener = ann.listener(); // Busy wait to stress cache coherency let mut attempts = 0; loop { attempts += 1; if let Some(value) = listener.try_listen() { return (i, value, attempts); } if attempts > 1_000_000 { return (i, -1, attempts); } } })); } // Announce after threads start spinning thread::sleep(Duration::from_nanos(10)); announcer.announce(iteration).unwrap(); for handle in handles { let (thread_id, value, attempts) = handle.join().unwrap(); if value != -1 { assert_eq!( value, iteration, "Thread {} got wrong value after {} attempts at iteration {}", thread_id, attempts, iteration ); } } } } /// Test: Race condition test: race sequential consistency /// /// Verifies: Verifies no race condition in race sequential consistency /// Category: race #[test] fn test_race_sequential_consistency() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let step1 = Arc::new(AtomicBool::new(false)); let step2 = Arc::new(AtomicBool::new(false)); let step1_clone = step1.clone(); let step2_clone = step2.clone(); let ann = announcement.clone(); let observer = thread::spawn(move || { // Wait for step1 while !step1_clone.load(Ordering::Acquire) { std::hint::spin_loop(); } let check1 = ann.is_announced(); // Wait for step2 while !step2_clone.load(Ordering::Acquire) { std::hint::spin_loop(); } let check2 = ann.is_announced(); (check1, check2) }); // Sequenced operations step1.store(true, Ordering::Release); thread::yield_now(); announcer.announce(iteration).unwrap(); thread::yield_now(); step2.store(true, Ordering::Release); let (before, after) = observer.join().unwrap(); // After must be true assert!( after, "Failed to see announcement after at iteration {}", iteration ); // Before might be false or true depending on timing if before { assert!(after); // If seen before, must be seen after } } } /// Test: Race condition test: race happens before /// /// Verifies: Verifies no race condition in race happens before /// Category: race #[test] fn test_race_happens_before() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let data = Arc::new(AtomicUsize::new(0)); let flag = Arc::new(AtomicBool::new(false)); let data1 = data.clone(); let flag1 = flag.clone(); let listener = announcement.listener(); let reader = thread::spawn(move || { // Wait for flag while !flag1.load(Ordering::Acquire) { std::hint::spin_loop(); } // Flag seen, check data and announcement let data_value = data1.load(Ordering::Relaxed); let announced = listener.try_listen(); (data_value, announced) }); let data2 = data.clone(); let flag2 = flag.clone(); let writer = thread::spawn(move || { // Write data data2.store(42, Ordering::Relaxed); // Announce announcer.announce(iteration).unwrap(); // Set flag with release flag2.store(true, Ordering::Release); }); writer.join().unwrap(); let (data_value, announced) = reader.join().unwrap(); // Due to happens-before, both should be visible assert_eq!(data_value, 42); assert_eq!(announced, Some(iteration)); } } /// Test: Race condition test: race aba problem /// /// Verifies: Verifies no race condition in race aba problem /// Category: race #[test] fn test_race_aba_problem() { for iteration in 0..10_000 { // This tests that even with rapid state changes, // the announcement remains consistent let (announcer, announcement) = Announcement::new(); let counter = Arc::new(AtomicUsize::new(0)); let counter1 = counter.clone(); let ann1 = announcement.clone(); let modifier = thread::spawn(move || { for _ in 0..10 { counter1.fetch_add(1, Ordering::Relaxed); let _ = ann1.is_announced(); counter1.fetch_sub(1, Ordering::Relaxed); } }); let counter2 = counter.clone(); let ann2 = announcement.clone(); let observer = thread::spawn(move || { let mut observations = vec![]; for _ in 0..10 { observations.push((counter2.load(Ordering::Relaxed), ann2.is_announced())); } observations }); // Announce in the middle thread::spawn(move || { thread::sleep(Duration::from_nanos((iteration % 10) as u64)); announcer.announce(iteration).unwrap(); }); modifier.join().unwrap(); let observations = observer.join().unwrap(); // is_announced should be monotonic despite ABA on counter let mut seen_announced = false; for (_, announced) in observations { if seen_announced { assert!(announced, "ABA problem detected at iteration {}", iteration); } if announced { seen_announced = true; } } } } /// Test: Race condition test: race store buffer /// /// Verifies: Verifies no race condition in race store buffer /// Category: race #[test] fn test_race_store_buffer() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let shared = Arc::new(AtomicUsize::new(0)); let shared1 = shared.clone(); let listener = announcement.listener(); let thread1 = thread::spawn(move || { // Might be in store buffer shared1.store(iteration, Ordering::Relaxed); // Check announcement listener.try_listen() }); let shared2 = shared.clone(); let thread2 = thread::spawn(move || { // Announce announcer.announce(iteration).unwrap(); // Read shared shared2.load(Ordering::Relaxed) }); let announced = thread1.join().unwrap(); let _loaded = thread2.join().unwrap(); // Values might not be synchronized due to store buffer // but announcement should work correctly if announced.is_some() { assert_eq!(announced, Some(iteration)); } } } // Continue with tests 21-50... // These would follow similar patterns, testing various race conditions /// Test: Race condition test: race ultimate /// /// Verifies: Verifies no race condition in race ultimate /// Category: race #[test] fn test_race_ultimate() { for iteration in 0..10_000 { let (announcer, announcement) = Announcement::new(); let barrier = Arc::new(Barrier::new(11)); let mut handles = vec![]; // Mix of all race-prone operations for i in 0..10 { let ann = announcement.clone(); let barrier = barrier.clone(); handles.push(thread::spawn(move || { barrier.wait(); match i % 5 { 0 => { // Rapid checks let mut checks = vec![]; for _ in 0..5 { checks.push(ann.is_announced()); } checks } 1 => { // Create and check let l = ann.listener(); vec![l.try_listen().is_some()] } 2 => { // Clone and check let l1 = ann.listener(); let l2 = l1.clone(); vec![l1.try_listen().is_some(), l2.try_listen().is_some()] } 3 => { // Multiple listeners let mut results = vec![]; for _ in 0..3 { let l = ann.listener(); results.push(l.try_listen().is_some()); } results } _ => { // Mixed operations let l = ann.listener(); vec![ ann.is_announced(), l.try_listen().is_some(), ann.is_announced(), ] } } })); } let barrier_announce = barrier.clone(); thread::spawn(move || { barrier_announce.wait(); // Random delay before announce match iteration % 4 { 0 => {} 1 => thread::yield_now(), 2 => thread::sleep(Duration::from_nanos(1)), _ => thread::sleep(Duration::from_nanos(10)), } announcer.announce(iteration).unwrap(); }); // Verify all complete without issues for handle in handles { handle.join().unwrap(); } } }