Files
announcement/fuzz/fuzz_targets/fuzz_concurrent.rs
T
swaits e8e55b7676 chore: initial release v0.1.0
Runtime-agnostic oneshot broadcast channel for Rust.
2025-10-13 19:36:37 -06:00

185 lines
6.0 KiB
Rust

#![no_main]
use libfuzzer_sys::fuzz_target;
use announcement::Announcement;
use std::sync::{Arc, Barrier};
use std::thread;
fuzz_target!(|data: &[u8]| {
if data.len() < 4 {
return;
}
// Extract test parameters from fuzz data
let num_threads = (data[0] % 8) as usize + 2; // 2-9 threads
let announce_delay = data[1] % 3; // 0-2 delay pattern
let pattern = data[2] % 4;
match pattern {
0 => {
// Test concurrent listeners
let (announcer, announcement) = Announcement::new();
let barrier = Arc::new(Barrier::new(num_threads + 1));
let handles: Vec<_> = (0..num_threads)
.map(|i| {
let listener = announcement.listener();
let barrier = barrier.clone();
thread::spawn(move || {
// Synchronize start
barrier.wait();
// All threads try to listen
let value = listener.try_listen();
// Return thread id and value
(i, value)
})
})
.collect();
// Start all threads together
barrier.wait();
// Announce after threads are started
match announce_delay {
0 => {
// Immediate announce
let _ = announcer.announce(42u32);
}
1 => {
// Short delay
thread::sleep(std::time::Duration::from_micros(10));
let _ = announcer.announce(42u32);
}
_ => {
// Drop without announcing
drop(announcer);
}
}
// Collect results
for handle in handles {
let (id, value) = handle.join().unwrap();
if announce_delay < 2 {
// Should get value if we announced
// (might be None if thread checked before announce)
if let Some(v) = value {
assert_eq!(v, 42u32, "Thread {} got wrong value", id);
}
}
}
}
1 => {
// Test concurrent clone and listener creation
let (announcer, announcement) = Announcement::new();
let handles: Vec<_> = (0..num_threads)
.map(|_| {
let ann_clone = announcement.clone();
thread::spawn(move || {
// Create listener from clone
let listener = ann_clone.listener();
// Try to read
listener.try_listen()
})
})
.collect();
// Announce while threads are creating listeners
let _ = announcer.announce(42u32);
// All should eventually see the value
for handle in handles {
let value = handle.join().unwrap();
// May or may not have seen the value depending on timing
if let Some(v) = value {
assert_eq!(v, 42u32);
}
}
}
2 => {
// Test concurrent is_announced checks
let (announcer, announcement) = Announcement::new();
let handles: Vec<_> = (0..num_threads)
.map(|_| {
let ann_clone = announcement.clone();
thread::spawn(move || {
// Check announced state multiple times
let mut states = vec![];
for _ in 0..10 {
states.push(ann_clone.is_announced());
}
states
})
})
.collect();
// Announce in the middle
thread::sleep(std::time::Duration::from_micros(5));
let _ = announcer.announce(42u32);
// Collect all state observations
for handle in handles {
let states = handle.join().unwrap();
// States should only transition from false to true, never back
let mut seen_true = false;
for state in states {
if seen_true {
assert!(state, "is_announced went from true to false");
}
if state {
seen_true = true;
}
}
}
}
_ => {
// Test concurrent close checks
let (announcer, announcement) = Announcement::<u32>::new();
let handles: Vec<_> = (0..num_threads)
.map(|_| {
let ann_clone = announcement.clone();
thread::spawn(move || {
// Check closed state multiple times
let mut states = vec![];
for _ in 0..10 {
states.push(ann_clone.is_closed());
}
states
})
})
.collect();
// Close in the middle
thread::sleep(std::time::Duration::from_micros(5));
announcer.close();
// Collect all state observations
for handle in handles {
let states = handle.join().unwrap();
// States should only transition from false to true, never back
let mut seen_true = false;
for state in states {
if seen_true {
assert!(state, "is_closed went from true to false");
}
if state {
seen_true = true;
}
}
}
}
}
});