247 lines
7.3 KiB
Rust
247 lines
7.3 KiB
Rust
//! Arc refcount stress test - rapid announce/drop cycles
|
|
//! Tests for memory leaks in rapid channel creation/destruction with Arc values.
|
|
|
|
use announcement::Announcement;
|
|
use std::sync::Arc;
|
|
use std::thread;
|
|
|
|
/// Test: Arc refcount: cycle detection
|
|
///
|
|
/// Verifies: Arc reference cycles are handled correctly
|
|
/// Category: refcount
|
|
// Test 1: Rapid Arc refcount cycles (single-threaded)
|
|
#[test]
|
|
fn test_arc_refcount_rapid_cycles() {
|
|
let data = Arc::new(vec![0u8; 1024]); // 1KB test data
|
|
let initial_count = Arc::strong_count(&data);
|
|
|
|
// Run 10,000 create/announce/drop cycles
|
|
for i in 0..10_000 {
|
|
{
|
|
let (announcer, announcement) = Announcement::new();
|
|
|
|
// Create a few listeners
|
|
let l1 = announcement.listener();
|
|
let l2 = announcement.listener();
|
|
let l3 = announcement.listener();
|
|
|
|
// Announce the Arc
|
|
announcer.announce(data.clone()).unwrap();
|
|
|
|
// Retrieve values
|
|
let _v1 = l1.try_listen().unwrap();
|
|
let _v2 = l2.try_listen().unwrap();
|
|
let _v3 = l3.try_listen().unwrap();
|
|
|
|
// Everything drops here when block ends
|
|
}
|
|
|
|
// Periodic check after everything has dropped
|
|
if i % 1000 == 0 {
|
|
let current_count = Arc::strong_count(&data);
|
|
assert_eq!(
|
|
current_count, initial_count,
|
|
"Refcount leak detected at iteration {}: expected {}, got {}",
|
|
i, initial_count, current_count
|
|
);
|
|
}
|
|
}
|
|
|
|
// Final verification
|
|
assert_eq!(
|
|
Arc::strong_count(&data),
|
|
initial_count,
|
|
"Final refcount mismatch after 10k cycles"
|
|
);
|
|
}
|
|
|
|
/// Test: Refcount test: arc refcount mixed drop patterns
|
|
///
|
|
/// Verifies: Verifies arc refcount mixed drop patterns
|
|
/// Category: refcount
|
|
// Test 2: Rapid Arc cycles with mixed drop patterns
|
|
#[test]
|
|
fn test_arc_refcount_mixed_drop_patterns() {
|
|
let data = Arc::new(vec![42u8; 512]);
|
|
let initial_count = Arc::strong_count(&data);
|
|
|
|
for i in 0..5_000 {
|
|
{
|
|
let (announcer, announcement) = Announcement::new();
|
|
|
|
// Create various numbers of listeners
|
|
let listener_count = (i % 10) + 1;
|
|
let mut listeners = Vec::new();
|
|
for _ in 0..listener_count {
|
|
listeners.push(announcement.listener());
|
|
}
|
|
|
|
// Announce
|
|
announcer.announce(data.clone()).unwrap();
|
|
|
|
// Drop listeners in different patterns
|
|
match i % 4 {
|
|
0 => drop(listeners), // Drop all at once
|
|
1 => {
|
|
// Drop one at a time
|
|
for l in listeners {
|
|
drop(l);
|
|
}
|
|
}
|
|
2 => {
|
|
// Retrieve then drop
|
|
let values: Vec<_> =
|
|
listeners.iter().map(|l| l.try_listen().unwrap()).collect();
|
|
drop(values);
|
|
drop(listeners);
|
|
}
|
|
_ => {
|
|
// Drop half, retrieve from rest
|
|
let mid = listeners.len() / 2;
|
|
drop(listeners.drain(0..mid).collect::<Vec<_>>());
|
|
for l in &listeners {
|
|
let _ = l.try_listen();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Block ends - everything drops including announcement
|
|
}
|
|
|
|
// Verify refcount periodically after everything dropped
|
|
if i % 500 == 0 {
|
|
assert_eq!(Arc::strong_count(&data), initial_count);
|
|
}
|
|
}
|
|
|
|
assert_eq!(Arc::strong_count(&data), initial_count);
|
|
}
|
|
|
|
/// Test: Arc refcount: cycle detection
|
|
///
|
|
/// Verifies: Arc reference cycles are handled correctly
|
|
/// Category: refcount
|
|
// Test 3: Concurrent Arc refcount stress
|
|
#[test]
|
|
fn test_arc_refcount_concurrent_cycles() {
|
|
let data = Arc::new(vec![0u8; 2048]);
|
|
let initial_count = Arc::strong_count(&data);
|
|
|
|
let mut handles = Vec::new();
|
|
|
|
// 8 threads all doing rapid cycles
|
|
for thread_id in 0..8 {
|
|
let data_clone = data.clone();
|
|
|
|
handles.push(thread::spawn(move || {
|
|
for i in 0..1_000 {
|
|
let (announcer, announcement) = Announcement::new();
|
|
|
|
let mut listeners = Vec::new();
|
|
for _ in 0..5 {
|
|
listeners.push(announcement.listener());
|
|
}
|
|
|
|
announcer.announce(data_clone.clone()).unwrap();
|
|
|
|
// Mix of operations
|
|
match i % 3 {
|
|
0 => {
|
|
// Just drop everything
|
|
drop(listeners);
|
|
}
|
|
1 => {
|
|
// Retrieve and verify
|
|
for l in &listeners {
|
|
let v = l.try_listen().unwrap();
|
|
assert_eq!(v.len(), 2048);
|
|
}
|
|
}
|
|
_ => {
|
|
// Mixed
|
|
let _ = listeners[0].try_listen();
|
|
drop(listeners);
|
|
}
|
|
}
|
|
|
|
if i % 100 == 0 && thread_id == 0 {
|
|
println!("Thread {} completed {} iterations", thread_id, i);
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
|
|
// Wait for all threads
|
|
for handle in handles {
|
|
handle.join().unwrap();
|
|
}
|
|
|
|
// Final refcount should be back to initial
|
|
assert_eq!(
|
|
Arc::strong_count(&data),
|
|
initial_count,
|
|
"Refcount leak after concurrent stress test"
|
|
);
|
|
}
|
|
|
|
/// Test: Refcount test: arc refcount with announcer drop
|
|
///
|
|
/// Verifies: Verifies arc refcount with announcer drop
|
|
/// Category: refcount
|
|
// Test 4: Arc refcount with announcer drop (no announce)
|
|
#[test]
|
|
fn test_arc_refcount_with_announcer_drop() {
|
|
let data = Arc::new(vec![1u8; 256]);
|
|
let initial_count = Arc::strong_count(&data);
|
|
|
|
for _ in 0..10_000 {
|
|
let (announcer, announcement) = Announcement::<Arc<Vec<u8>>>::new();
|
|
|
|
let _l1 = announcement.listener();
|
|
let _l2 = announcement.listener();
|
|
|
|
// Drop announcer WITHOUT announcing
|
|
drop(announcer);
|
|
|
|
// Listeners should see closed, no Arc refcount change for data
|
|
// since it was never announced
|
|
}
|
|
|
|
// Refcount should be unchanged
|
|
assert_eq!(Arc::strong_count(&data), initial_count);
|
|
}
|
|
|
|
/// Test: Refcount test: arc refcount with listener cloning
|
|
///
|
|
/// Verifies: Verifies arc refcount with listener cloning
|
|
/// Category: refcount
|
|
// Test 5: Arc refcount with listener cloning stress
|
|
#[test]
|
|
fn test_arc_refcount_with_listener_cloning() {
|
|
let data = Arc::new(vec![7u8; 512]);
|
|
let initial_count = Arc::strong_count(&data);
|
|
|
|
for _ in 0..5_000 {
|
|
let (announcer, announcement) = Announcement::new();
|
|
|
|
let l1 = announcement.listener();
|
|
|
|
// Clone listener many times
|
|
let mut clones = vec![l1.clone()];
|
|
for _ in 0..10 {
|
|
clones.push(clones[0].clone());
|
|
}
|
|
|
|
announcer.announce(data.clone()).unwrap();
|
|
|
|
// All clones retrieve
|
|
for l in &clones {
|
|
let _v = l.try_listen().unwrap();
|
|
}
|
|
|
|
// Everything drops
|
|
}
|
|
|
|
assert_eq!(Arc::strong_count(&data), initial_count);
|
|
}
|