1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! Concurrent operation edge cases for ass-editor.
//!
//! Exercised only when the `multi-thread` and `std` features are enabled.
#[cfg(all(feature = "multi-thread", feature = "std"))]
mod concurrent_tests {
use ass_editor::{EditorSessionManager, ExtensionManager, Position};
use std::sync::{Arc, Barrier};
use std::thread;
#[test]
fn test_concurrent_config_access() {
use std::sync::Arc;
// Use Arc to share the manager instead of cloning
let manager = Arc::new(ExtensionManager::new());
let barrier = Arc::new(Barrier::new(10));
let mut handles = vec![];
// Spawn 10 threads that all try to modify config simultaneously
for i in 0..10 {
let manager_ref = Arc::clone(&manager);
let barrier_clone = barrier.clone();
let handle = thread::spawn(move || {
barrier_clone.wait();
// Each thread sets multiple config values
// We can't modify through Arc<ExtensionManager> directly,
// so this test verifies read-only concurrent access
for j in 0..100 {
let key = format!("thread_{i}_key_{j}");
let _value = manager_ref.get_config(&key);
}
});
handles.push(handle);
}
// Set up test data first
// Since we can't get mutable access through Arc, pre-populate
for i in 0..10 {
for j in 0..10 {
// Reduce to avoid too many keys
let _key = format!("thread_{i}_key_{j}");
// Note: We can't actually set config through Arc<ExtensionManager>
// This test is mainly to verify the manager doesn't panic under concurrent read access
}
}
// Wait for all threads
for handle in handles {
handle.join().unwrap();
}
// Test passes if no panics occurred during concurrent access
}
#[cfg(feature = "std")]
#[test]
fn test_concurrent_session_operations() {
use std::sync::atomic::{AtomicUsize, Ordering};
let _manager = EditorSessionManager::new();
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = vec![];
// Create sessions concurrently
// Note: This test demonstrates the design limitation -
// we can't easily share a mutable session manager across threads
// without the Clone trait. This suggests the API needs improvement.
for i in 0..5 {
let counter_clone = counter.clone();
let handle = thread::spawn(move || {
// Each thread creates its own manager since we can't clone
let mut local_manager = EditorSessionManager::new();
for j in 0..20 {
let session_name = format!("session_{i}_{j}");
let result = local_manager.create_session(session_name.clone());
// Creation might fail due to session limit (50 by default)
if result.is_ok() {
// Modify the session
local_manager
.with_document_mut(&session_name, |doc| {
doc.insert(Position::new(0), &format!("Thread {i} Doc {j}\n"))
})
.unwrap();
counter_clone.fetch_add(1, Ordering::Relaxed);
}
}
});
handles.push(handle);
}
// Wait for completion
for handle in handles {
handle.join().unwrap();
}
// Verify sessions were created in each thread
let created_count = counter.load(Ordering::Relaxed);
assert!(created_count > 0); // Should create some sessions across all threads
// Note: Since each thread uses its own manager, we can't verify
// the sessions on the original manager. This test mainly verifies
// that concurrent session creation doesn't panic or deadlock.
}
}