Skip to main content

scirs2_core/concurrent/
mod.rs

1//! Concurrent data structures for safe shared-state parallelism.
2//!
3//! This module provides high-performance, lock-based concurrent data structures:
4//!
5//! - [`ConcurrentHashMap`] — sharded-lock hash map for high-throughput concurrent access
6//! - [`BoundedQueue`] — bounded MPMC (multi-producer, multi-consumer) queue
7//! - [`ConcurrentAccumulator`] — lock-free parallel accumulator for reductions
8//! - [`WriterPreferenceRwLock`] — read-write lock that gives priority to writers
9//! - [`DoubleBuffer`] — double-buffered exchange for producer/consumer patterns
10//!
11//! All structures are `Send + Sync` and avoid `unwrap()` in favour of explicit error handling.
12//!
13//! # Example
14//!
15//! ```rust
16//! use scirs2_core::concurrent::{ConcurrentHashMap, BoundedQueue};
17//!
18//! // Concurrent hash map
19//! let map: ConcurrentHashMap<String, u64> = ConcurrentHashMap::new();
20//! map.insert("key".into(), 42);
21//! assert_eq!(map.get(&"key".to_string()), Some(42));
22//!
23//! // Bounded MPMC queue
24//! let queue: BoundedQueue<i32> = BoundedQueue::new(16);
25//! queue.push(1).expect("should succeed");
26//! assert_eq!(queue.pop(), Some(1));
27//! ```
28
29use std::collections::HashMap;
30use std::hash::{BuildHasher, Hash, Hasher};
31use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
32use std::sync::{Arc, Condvar, Mutex, MutexGuard};
33use std::time::Duration;
34
35use crate::error::{CoreError, CoreResult, ErrorContext};
36
37// ===========================================================================
38// ConcurrentHashMap (sharded lock)
39// ===========================================================================
40
41/// Default number of shards for [`ConcurrentHashMap`].
42const DEFAULT_SHARD_COUNT: usize = 64;
43
44/// A concurrent hash map using shard-level locking for high throughput.
45///
46/// The map is split into `N` independent shards, each protected by its own
47/// mutex.  This dramatically reduces contention compared to a single global
48/// lock when many threads operate on different keys simultaneously.
49pub struct ConcurrentHashMap<K, V, S = std::hash::RandomState> {
50    shards: Vec<Mutex<HashMap<K, V, S>>>,
51    shard_count: usize,
52    hash_builder: S,
53    len: AtomicUsize,
54}
55
56impl<K, V> ConcurrentHashMap<K, V, std::hash::RandomState>
57where
58    K: Eq + Hash + Clone,
59    V: Clone,
60{
61    /// Create a new concurrent hash map with the default number of shards.
62    pub fn new() -> Self {
63        Self::with_shard_count(DEFAULT_SHARD_COUNT)
64    }
65
66    /// Create a concurrent hash map with the specified number of shards.
67    pub fn with_shard_count(n: usize) -> Self {
68        let shard_count = n.max(1);
69        let hash_builder = std::hash::RandomState::new();
70        let shards = (0..shard_count)
71            .map(|_| Mutex::new(HashMap::with_hasher(hash_builder.clone())))
72            .collect();
73        Self {
74            shards,
75            shard_count,
76            hash_builder,
77            len: AtomicUsize::new(0),
78        }
79    }
80}
81
82impl<K, V, S> ConcurrentHashMap<K, V, S>
83where
84    K: Eq + Hash + Clone,
85    V: Clone,
86    S: BuildHasher + Clone,
87{
88    fn shard_index(&self, key: &K) -> usize {
89        (self.hash_builder.hash_one(&key) as usize) % self.shard_count
90    }
91
92    fn lock_shard(&self, idx: usize) -> CoreResult<MutexGuard<'_, HashMap<K, V, S>>> {
93        self.shards[idx].lock().map_err(|e| {
94            CoreError::ComputationError(ErrorContext::new(format!(
95                "concurrent hash map shard {idx} mutex poisoned: {e}"
96            )))
97        })
98    }
99
100    /// Insert a key-value pair, returning the previous value if the key already existed.
101    pub fn insert(&self, key: K, value: V) -> Option<V> {
102        let idx = self.shard_index(&key);
103        let mut shard = match self.lock_shard(idx) {
104            Ok(s) => s,
105            Err(_) => return None,
106        };
107        let prev = shard.insert(key, value);
108        if prev.is_none() {
109            self.len.fetch_add(1, Ordering::Release);
110        }
111        prev
112    }
113
114    /// Retrieve a clone of the value associated with the key.
115    pub fn get(&self, key: &K) -> Option<V> {
116        let idx = self.shard_index(key);
117        let shard = match self.lock_shard(idx) {
118            Ok(s) => s,
119            Err(_) => return None,
120        };
121        shard.get(key).cloned()
122    }
123
124    /// Remove a key and return its value.
125    pub fn remove(&self, key: &K) -> Option<V> {
126        let idx = self.shard_index(key);
127        let mut shard = match self.lock_shard(idx) {
128            Ok(s) => s,
129            Err(_) => return None,
130        };
131        let removed = shard.remove(key);
132        if removed.is_some() {
133            self.len.fetch_sub(1, Ordering::Release);
134        }
135        removed
136    }
137
138    /// Check whether the map contains the key.
139    pub fn contains_key(&self, key: &K) -> bool {
140        let idx = self.shard_index(key);
141        match self.lock_shard(idx) {
142            Ok(shard) => shard.contains_key(key),
143            Err(_) => false,
144        }
145    }
146
147    /// Approximate number of entries (may be slightly stale under contention).
148    pub fn len(&self) -> usize {
149        self.len.load(Ordering::Acquire)
150    }
151
152    /// Whether the map is empty.
153    pub fn is_empty(&self) -> bool {
154        self.len() == 0
155    }
156
157    /// Apply a function to the value associated with `key`, returning the result.
158    ///
159    /// If the key doesn't exist, returns `None`.
160    pub fn get_and_modify<F, R>(&self, key: &K, f: F) -> Option<R>
161    where
162        F: FnOnce(&mut V) -> R,
163    {
164        let idx = self.shard_index(key);
165        let mut shard = match self.lock_shard(idx) {
166            Ok(s) => s,
167            Err(_) => return None,
168        };
169        shard.get_mut(key).map(f)
170    }
171
172    /// Insert if the key does not already exist, using a closure to create the value.
173    ///
174    /// Returns a clone of the existing or newly-inserted value.
175    pub fn get_or_insert_with<F>(&self, key: K, f: F) -> V
176    where
177        F: FnOnce() -> V,
178    {
179        let idx = self.shard_index(&key);
180        let mut shard = match self.lock_shard(idx) {
181            Ok(s) => s,
182            Err(_) => return f(),
183        };
184        if let Some(existing) = shard.get(&key) {
185            return existing.clone();
186        }
187        let value = f();
188        let cloned = value.clone();
189        shard.insert(key, value);
190        self.len.fetch_add(1, Ordering::Release);
191        cloned
192    }
193
194    /// Collect all keys into a Vec.
195    pub fn keys(&self) -> Vec<K> {
196        let mut result = Vec::new();
197        for shard_mutex in &self.shards {
198            if let Ok(shard) = shard_mutex.lock() {
199                result.extend(shard.keys().cloned());
200            }
201        }
202        result
203    }
204
205    /// Clear all entries.
206    pub fn clear(&self) {
207        for shard_mutex in &self.shards {
208            if let Ok(mut shard) = shard_mutex.lock() {
209                shard.clear();
210            }
211        }
212        self.len.store(0, Ordering::Release);
213    }
214}
215
216// Safety: The sharded mutexes ensure thread-safe access.
217unsafe impl<K: Send, V: Send, S: Send> Send for ConcurrentHashMap<K, V, S> {}
218unsafe impl<K: Send + Sync, V: Send + Sync, S: Send + Sync> Sync for ConcurrentHashMap<K, V, S> {}
219
220impl<K, V> Default for ConcurrentHashMap<K, V, std::hash::RandomState>
221where
222    K: Eq + Hash + Clone,
223    V: Clone,
224{
225    fn default() -> Self {
226        Self::new()
227    }
228}
229
230// ===========================================================================
231// BoundedQueue (MPMC)
232// ===========================================================================
233
234/// A bounded, multi-producer, multi-consumer (MPMC) queue.
235///
236/// Pushing to a full queue returns an error rather than blocking.
237/// Popping from an empty queue returns `None`.
238/// Blocking variants (`push_blocking`, `pop_blocking`) are available.
239pub struct BoundedQueue<T> {
240    buffer: Mutex<std::collections::VecDeque<T>>,
241    capacity: usize,
242    not_empty: Condvar,
243    not_full: Condvar,
244    len: AtomicUsize,
245    closed: AtomicBool,
246}
247
248impl<T> BoundedQueue<T> {
249    /// Create a new bounded queue with the given capacity.
250    pub fn new(capacity: usize) -> Self {
251        let cap = capacity.max(1);
252        Self {
253            buffer: Mutex::new(std::collections::VecDeque::with_capacity(cap)),
254            capacity: cap,
255            not_empty: Condvar::new(),
256            not_full: Condvar::new(),
257            len: AtomicUsize::new(0),
258            closed: AtomicBool::new(false),
259        }
260    }
261
262    /// Try to push an item, returning `Err(item)` if the queue is full or closed.
263    pub fn push(&self, item: T) -> Result<(), T> {
264        if self.closed.load(Ordering::Acquire) {
265            return Err(item);
266        }
267        let mut buf = match self.buffer.lock() {
268            Ok(b) => b,
269            Err(_) => return Err(item),
270        };
271        if buf.len() >= self.capacity {
272            return Err(item);
273        }
274        buf.push_back(item);
275        self.len.fetch_add(1, Ordering::Release);
276        self.not_empty.notify_one();
277        Ok(())
278    }
279
280    /// Push an item, blocking until space is available or the queue is closed.
281    pub fn push_blocking(&self, item: T) -> CoreResult<()> {
282        self.push_blocking_timeout(item, None)
283    }
284
285    /// Push an item with an optional timeout.
286    pub fn push_blocking_timeout(&self, mut item: T, timeout: Option<Duration>) -> CoreResult<()> {
287        let deadline = timeout.map(|d| std::time::Instant::now() + d);
288
289        loop {
290            if self.closed.load(Ordering::Acquire) {
291                return Err(CoreError::ComputationError(ErrorContext::new(
292                    "queue is closed".to_string(),
293                )));
294            }
295
296            let mut buf = self.buffer.lock().map_err(|e| {
297                CoreError::ComputationError(ErrorContext::new(format!("queue mutex poisoned: {e}")))
298            })?;
299
300            if buf.len() < self.capacity {
301                buf.push_back(item);
302                self.len.fetch_add(1, Ordering::Release);
303                self.not_empty.notify_one();
304                return Ok(());
305            }
306
307            // Wait for space.
308            if let Some(dl) = deadline {
309                let remaining = dl.saturating_duration_since(std::time::Instant::now());
310                if remaining.is_zero() {
311                    return Err(CoreError::ComputationError(ErrorContext::new(
312                        "push timed out".to_string(),
313                    )));
314                }
315                let (b, timeout_result) =
316                    self.not_full.wait_timeout(buf, remaining).map_err(|e| {
317                        CoreError::ComputationError(ErrorContext::new(format!(
318                            "condvar wait failed: {e}"
319                        )))
320                    })?;
321                drop(b);
322                if timeout_result.timed_out() {
323                    return Err(CoreError::ComputationError(ErrorContext::new(
324                        "push timed out".to_string(),
325                    )));
326                }
327            } else {
328                let _b = self.not_full.wait(buf).map_err(|e| {
329                    CoreError::ComputationError(ErrorContext::new(format!(
330                        "condvar wait failed: {e}"
331                    )))
332                })?;
333            }
334        }
335    }
336
337    /// Try to pop an item, returning `None` if the queue is empty.
338    pub fn pop(&self) -> Option<T> {
339        let mut buf = match self.buffer.lock() {
340            Ok(b) => b,
341            Err(_) => return None,
342        };
343        let item = buf.pop_front();
344        if item.is_some() {
345            self.len.fetch_sub(1, Ordering::Release);
346            self.not_full.notify_one();
347        }
348        item
349    }
350
351    /// Pop an item, blocking until one is available or the queue is closed.
352    pub fn pop_blocking(&self) -> CoreResult<Option<T>> {
353        self.pop_blocking_timeout(None)
354    }
355
356    /// Pop an item with an optional timeout.
357    pub fn pop_blocking_timeout(&self, timeout: Option<Duration>) -> CoreResult<Option<T>> {
358        let deadline = timeout.map(|d| std::time::Instant::now() + d);
359
360        loop {
361            let mut buf = self.buffer.lock().map_err(|e| {
362                CoreError::ComputationError(ErrorContext::new(format!("queue mutex poisoned: {e}")))
363            })?;
364
365            if let Some(item) = buf.pop_front() {
366                self.len.fetch_sub(1, Ordering::Release);
367                self.not_full.notify_one();
368                return Ok(Some(item));
369            }
370
371            if self.closed.load(Ordering::Acquire) {
372                return Ok(None);
373            }
374
375            if let Some(dl) = deadline {
376                let remaining = dl.saturating_duration_since(std::time::Instant::now());
377                if remaining.is_zero() {
378                    return Ok(None); // timed out
379                }
380                let (b, timeout_result) =
381                    self.not_empty.wait_timeout(buf, remaining).map_err(|e| {
382                        CoreError::ComputationError(ErrorContext::new(format!(
383                            "condvar wait failed: {e}"
384                        )))
385                    })?;
386                drop(b);
387                if timeout_result.timed_out() {
388                    return Ok(None);
389                }
390            } else {
391                let _b = self.not_empty.wait(buf).map_err(|e| {
392                    CoreError::ComputationError(ErrorContext::new(format!(
393                        "condvar wait failed: {e}"
394                    )))
395                })?;
396            }
397        }
398    }
399
400    /// Number of items currently in the queue.
401    pub fn len(&self) -> usize {
402        self.len.load(Ordering::Acquire)
403    }
404
405    /// Whether the queue is empty.
406    pub fn is_empty(&self) -> bool {
407        self.len() == 0
408    }
409
410    /// Maximum capacity of the queue.
411    pub fn capacity(&self) -> usize {
412        self.capacity
413    }
414
415    /// Close the queue; no more pushes are accepted.
416    ///
417    /// Blocked consumers will wake up and receive `None`.
418    pub fn close(&self) {
419        self.closed.store(true, Ordering::Release);
420        self.not_empty.notify_all();
421        self.not_full.notify_all();
422    }
423
424    /// Whether the queue is closed.
425    pub fn is_closed(&self) -> bool {
426        self.closed.load(Ordering::Acquire)
427    }
428}
429
430unsafe impl<T: Send> Send for BoundedQueue<T> {}
431unsafe impl<T: Send> Sync for BoundedQueue<T> {}
432
433// ===========================================================================
434// ConcurrentAccumulator (for parallel reductions)
435// ===========================================================================
436
437/// A concurrent accumulator for parallel reductions.
438///
439/// Supports atomic `f64` accumulation via sharded slots to reduce contention,
440/// plus a generic `T` accumulator that uses a mutex.
441pub struct ConcurrentAccumulator<T: Clone> {
442    /// Sharded accumulators to reduce contention.
443    shards: Vec<Mutex<T>>,
444    shard_count: usize,
445    /// Combining function: `combine(accumulator, new_value) -> accumulator`.
446    combiner: Arc<dyn Fn(T, T) -> T + Send + Sync>,
447    /// Identity / zero element for the accumulation.
448    identity: T,
449    /// Counter for round-robin shard selection.
450    counter: AtomicUsize,
451}
452
453impl<T: Clone + Send + Sync + 'static> ConcurrentAccumulator<T> {
454    /// Create a new accumulator with the given identity element and combiner function.
455    ///
456    /// `shard_count` controls the number of independent accumulator slots (more = less contention).
457    pub fn new<F>(identity: T, combiner: F, shard_count: usize) -> Self
458    where
459        F: Fn(T, T) -> T + Send + Sync + 'static,
460    {
461        let sc = shard_count.max(1);
462        let shards = (0..sc).map(|_| Mutex::new(identity.clone())).collect();
463        Self {
464            shards,
465            shard_count: sc,
466            combiner: Arc::new(combiner),
467            identity,
468            counter: AtomicUsize::new(0),
469        }
470    }
471
472    /// Accumulate a value into one of the shards.
473    pub fn accumulate(&self, value: T) {
474        let idx = self.counter.fetch_add(1, Ordering::Relaxed) % self.shard_count;
475        if let Ok(mut shard) = self.shards[idx].lock() {
476            let old = shard.clone();
477            *shard = (self.combiner)(old, value);
478        }
479    }
480
481    /// Combine all shards and return the final accumulated value.
482    pub fn result(&self) -> T {
483        let mut acc = self.identity.clone();
484        for shard_mutex in &self.shards {
485            if let Ok(shard) = shard_mutex.lock() {
486                acc = (self.combiner)(acc, shard.clone());
487            }
488        }
489        acc
490    }
491
492    /// Reset all shards to the identity element.
493    pub fn reset(&self) {
494        for shard_mutex in &self.shards {
495            if let Ok(mut shard) = shard_mutex.lock() {
496                *shard = self.identity.clone();
497            }
498        }
499        self.counter.store(0, Ordering::Relaxed);
500    }
501}
502
503unsafe impl<T: Clone + Send> Send for ConcurrentAccumulator<T> {}
504unsafe impl<T: Clone + Send + Sync> Sync for ConcurrentAccumulator<T> {}
505
506// ---------------------------------------------------------------------------
507// Specialised f64 accumulator using AtomicU64 for lock-free sum
508// ---------------------------------------------------------------------------
509
510/// A lock-free accumulator specialised for `f64` summation.
511///
512/// Uses atomic compare-and-swap on the bits of an `f64` for true lock-free operation.
513pub struct AtomicF64Accumulator {
514    bits: AtomicU64,
515    count: AtomicU64,
516}
517
518impl AtomicF64Accumulator {
519    /// Create a new accumulator initialised to 0.0.
520    pub fn new() -> Self {
521        Self {
522            bits: AtomicU64::new(0.0_f64.to_bits()),
523            count: AtomicU64::new(0),
524        }
525    }
526
527    /// Atomically add `value` to the accumulator.
528    pub fn add(&self, value: f64) {
529        loop {
530            let current_bits = self.bits.load(Ordering::Acquire);
531            let current = f64::from_bits(current_bits);
532            let new = current + value;
533            let new_bits = new.to_bits();
534            if self
535                .bits
536                .compare_exchange_weak(current_bits, new_bits, Ordering::AcqRel, Ordering::Acquire)
537                .is_ok()
538            {
539                self.count.fetch_add(1, Ordering::Relaxed);
540                return;
541            }
542        }
543    }
544
545    /// Read the current accumulated value.
546    pub fn value(&self) -> f64 {
547        f64::from_bits(self.bits.load(Ordering::Acquire))
548    }
549
550    /// Number of values accumulated so far.
551    pub fn count(&self) -> u64 {
552        self.count.load(Ordering::Acquire)
553    }
554
555    /// Reset to 0.0.
556    pub fn reset(&self) {
557        self.bits.store(0.0_f64.to_bits(), Ordering::Release);
558        self.count.store(0, Ordering::Release);
559    }
560}
561
562impl Default for AtomicF64Accumulator {
563    fn default() -> Self {
564        Self::new()
565    }
566}
567
568// ===========================================================================
569// WriterPreferenceRwLock
570// ===========================================================================
571
572/// A read-write lock that gives priority to writers.
573///
574/// When a writer is waiting, new readers are blocked until the writer has been serviced.
575/// This prevents writer starvation under heavy read load.
576pub struct WriterPreferenceRwLock<T> {
577    data: std::sync::RwLock<T>,
578    /// Number of writers waiting.
579    writers_waiting: AtomicUsize,
580    /// Gate that readers must pass through; blocked when writers are waiting.
581    reader_gate: Mutex<()>,
582    reader_gate_cv: Condvar,
583}
584
585impl<T> WriterPreferenceRwLock<T> {
586    /// Create a new writer-preference RwLock wrapping `data`.
587    pub fn new(data: T) -> Self {
588        Self {
589            data: std::sync::RwLock::new(data),
590            writers_waiting: AtomicUsize::new(0),
591            reader_gate: Mutex::new(()),
592            reader_gate_cv: Condvar::new(),
593        }
594    }
595
596    /// Acquire a read lock.
597    ///
598    /// If a writer is waiting, this will block until the writer has been serviced.
599    pub fn read(&self) -> CoreResult<ReadGuard<'_, T>> {
600        // Wait until no writers are waiting.
601        {
602            let mut gate = self.reader_gate.lock().map_err(|e| {
603                CoreError::ComputationError(ErrorContext::new(format!("gate mutex poisoned: {e}")))
604            })?;
605            while self.writers_waiting.load(Ordering::Acquire) > 0 {
606                gate = self.reader_gate_cv.wait(gate).map_err(|e| {
607                    CoreError::ComputationError(ErrorContext::new(format!(
608                        "condvar wait failed: {e}"
609                    )))
610                })?;
611            }
612        }
613        let guard = self.data.read().map_err(|e| {
614            CoreError::ComputationError(ErrorContext::new(format!("rwlock poisoned: {e}")))
615        })?;
616        Ok(ReadGuard { guard })
617    }
618
619    /// Acquire a write lock.
620    ///
621    /// Writers are given priority: once a writer starts waiting,
622    /// new readers are blocked until the writer is serviced.
623    pub fn write(&self) -> CoreResult<WriteGuard<'_, T>> {
624        self.writers_waiting.fetch_add(1, Ordering::Release);
625        let guard = self.data.write().map_err(|e| {
626            self.writers_waiting.fetch_sub(1, Ordering::Release);
627            CoreError::ComputationError(ErrorContext::new(format!("rwlock poisoned: {e}")))
628        })?;
629        self.writers_waiting.fetch_sub(1, Ordering::Release);
630        // Wake blocked readers now that this writer has the lock.
631        self.reader_gate_cv.notify_all();
632        Ok(WriteGuard { guard })
633    }
634
635    /// Try to acquire a read lock without blocking.
636    pub fn try_read(&self) -> CoreResult<Option<ReadGuard<'_, T>>> {
637        if self.writers_waiting.load(Ordering::Acquire) > 0 {
638            return Ok(None);
639        }
640        match self.data.try_read() {
641            Ok(guard) => Ok(Some(ReadGuard { guard })),
642            Err(std::sync::TryLockError::WouldBlock) => Ok(None),
643            Err(std::sync::TryLockError::Poisoned(e)) => Err(CoreError::ComputationError(
644                ErrorContext::new(format!("rwlock poisoned: {e}")),
645            )),
646        }
647    }
648
649    /// Try to acquire a write lock without blocking.
650    pub fn try_write(&self) -> CoreResult<Option<WriteGuard<'_, T>>> {
651        match self.data.try_write() {
652            Ok(guard) => Ok(Some(WriteGuard { guard })),
653            Err(std::sync::TryLockError::WouldBlock) => Ok(None),
654            Err(std::sync::TryLockError::Poisoned(e)) => Err(CoreError::ComputationError(
655                ErrorContext::new(format!("rwlock poisoned: {e}")),
656            )),
657        }
658    }
659}
660
661unsafe impl<T: Send> Send for WriterPreferenceRwLock<T> {}
662unsafe impl<T: Send + Sync> Sync for WriterPreferenceRwLock<T> {}
663
664/// RAII read guard for [`WriterPreferenceRwLock`].
665pub struct ReadGuard<'a, T> {
666    guard: std::sync::RwLockReadGuard<'a, T>,
667}
668
669impl<T> std::ops::Deref for ReadGuard<'_, T> {
670    type Target = T;
671    fn deref(&self) -> &T {
672        &self.guard
673    }
674}
675
676/// RAII write guard for [`WriterPreferenceRwLock`].
677pub struct WriteGuard<'a, T> {
678    guard: std::sync::RwLockWriteGuard<'a, T>,
679}
680
681impl<T> std::ops::Deref for WriteGuard<'_, T> {
682    type Target = T;
683    fn deref(&self) -> &T {
684        &self.guard
685    }
686}
687
688impl<T> std::ops::DerefMut for WriteGuard<'_, T> {
689    fn deref_mut(&mut self) -> &mut T {
690        &mut self.guard
691    }
692}
693
694// ===========================================================================
695// DoubleBuffer
696// ===========================================================================
697
698/// A double-buffered exchange for efficient producer/consumer handoff.
699///
700/// The producer writes to the "back" buffer while the consumer reads from the
701/// "front" buffer.  When the producer is done writing, it swaps the buffers
702/// (atomically from the consumer's perspective).
703pub struct DoubleBuffer<T> {
704    buffers: [Mutex<T>; 2],
705    /// 0 or 1 – index of the "front" (consumer) buffer.
706    front_index: AtomicUsize,
707    /// Signals that a new frame is ready.
708    new_frame: Condvar,
709    new_frame_mutex: Mutex<bool>,
710}
711
712impl<T: Clone> DoubleBuffer<T> {
713    /// Create a double buffer with two copies of `initial`.
714    pub fn new(initial: T) -> Self {
715        Self {
716            buffers: [Mutex::new(initial.clone()), Mutex::new(initial)],
717            front_index: AtomicUsize::new(0),
718            new_frame: Condvar::new(),
719            new_frame_mutex: Mutex::new(false),
720        }
721    }
722
723    /// Read a clone of the current front buffer.
724    pub fn read_front(&self) -> CoreResult<T> {
725        let idx = self.front_index.load(Ordering::Acquire);
726        let guard = self.buffers[idx].lock().map_err(|e| {
727            CoreError::ComputationError(ErrorContext::new(format!("buffer mutex poisoned: {e}")))
728        })?;
729        Ok(guard.clone())
730    }
731
732    /// Write to the back buffer via a closure, then swap.
733    pub fn write_and_swap<F>(&self, f: F) -> CoreResult<()>
734    where
735        F: FnOnce(&mut T),
736    {
737        let front = self.front_index.load(Ordering::Acquire);
738        let back = 1 - front;
739        {
740            let mut guard = self.buffers[back].lock().map_err(|e| {
741                CoreError::ComputationError(ErrorContext::new(format!(
742                    "buffer mutex poisoned: {e}"
743                )))
744            })?;
745            f(&mut guard);
746        }
747        // Swap front and back.
748        self.front_index.store(back, Ordering::Release);
749        // Signal consumers.
750        if let Ok(mut flag) = self.new_frame_mutex.lock() {
751            *flag = true;
752            self.new_frame.notify_all();
753        }
754        Ok(())
755    }
756
757    /// Block until a new frame is available, then read it.
758    pub fn wait_and_read(&self, timeout: Duration) -> CoreResult<Option<T>> {
759        let mut flag = self.new_frame_mutex.lock().map_err(|e| {
760            CoreError::ComputationError(ErrorContext::new(format!("mutex poisoned: {e}")))
761        })?;
762        if !*flag {
763            let (f, timeout_result) = self.new_frame.wait_timeout(flag, timeout).map_err(|e| {
764                CoreError::ComputationError(ErrorContext::new(format!("condvar wait failed: {e}")))
765            })?;
766            flag = f;
767            if timeout_result.timed_out() && !*flag {
768                return Ok(None);
769            }
770        }
771        *flag = false;
772        drop(flag);
773        self.read_front().map(Some)
774    }
775
776    /// Replace the back buffer entirely, then swap.
777    pub fn publish(&self, value: T) -> CoreResult<()> {
778        self.write_and_swap(|buf| {
779            *buf = value;
780        })
781    }
782}
783
784unsafe impl<T: Send> Send for DoubleBuffer<T> {}
785unsafe impl<T: Send + Sync> Sync for DoubleBuffer<T> {}
786
787// ===========================================================================
788// Tests
789// ===========================================================================
790
791#[cfg(test)]
792mod tests {
793    use super::*;
794    use std::sync::Arc;
795    use std::thread;
796
797    // ----- ConcurrentHashMap -----
798
799    #[test]
800    fn test_hashmap_basic() {
801        let map: ConcurrentHashMap<String, i32> = ConcurrentHashMap::new();
802        assert!(map.is_empty());
803
804        map.insert("a".to_string(), 1);
805        map.insert("b".to_string(), 2);
806        assert_eq!(map.len(), 2);
807        assert_eq!(map.get(&"a".to_string()), Some(1));
808        assert_eq!(map.get(&"b".to_string()), Some(2));
809        assert_eq!(map.get(&"c".to_string()), None);
810    }
811
812    #[test]
813    fn test_hashmap_remove() {
814        let map: ConcurrentHashMap<String, i32> = ConcurrentHashMap::new();
815        map.insert("x".to_string(), 10);
816        assert_eq!(map.remove(&"x".to_string()), Some(10));
817        assert_eq!(map.len(), 0);
818        assert!(map.is_empty());
819    }
820
821    #[test]
822    fn test_hashmap_concurrent() {
823        let map = Arc::new(ConcurrentHashMap::<u64, u64>::new());
824        let mut handles = Vec::new();
825
826        for t in 0..8 {
827            let m = map.clone();
828            handles.push(thread::spawn(move || {
829                for i in 0..1000 {
830                    let key = t * 1000 + i;
831                    m.insert(key, key * 2);
832                }
833            }));
834        }
835
836        for h in handles {
837            h.join().expect("thread join");
838        }
839
840        assert_eq!(map.len(), 8000);
841        assert_eq!(map.get(&0), Some(0));
842        assert_eq!(map.get(&7999), Some(7999 * 2));
843    }
844
845    #[test]
846    fn test_hashmap_get_or_insert_with() {
847        let map: ConcurrentHashMap<String, i32> = ConcurrentHashMap::new();
848        let v = map.get_or_insert_with("key".to_string(), || 42);
849        assert_eq!(v, 42);
850        let v2 = map.get_or_insert_with("key".to_string(), || 99);
851        assert_eq!(v2, 42); // already inserted
852    }
853
854    #[test]
855    fn test_hashmap_get_and_modify() {
856        let map: ConcurrentHashMap<String, i32> = ConcurrentHashMap::new();
857        map.insert("k".to_string(), 10);
858        let result = map.get_and_modify(&"k".to_string(), |v| {
859            *v += 5;
860            *v
861        });
862        assert_eq!(result, Some(15));
863    }
864
865    #[test]
866    fn test_hashmap_keys_and_clear() {
867        let map: ConcurrentHashMap<u32, u32> = ConcurrentHashMap::new();
868        for i in 0..10 {
869            map.insert(i, i);
870        }
871        let keys = map.keys();
872        assert_eq!(keys.len(), 10);
873        map.clear();
874        assert!(map.is_empty());
875    }
876
877    #[test]
878    fn test_hashmap_contains_key() {
879        let map: ConcurrentHashMap<String, i32> = ConcurrentHashMap::new();
880        map.insert("hello".into(), 1);
881        assert!(map.contains_key(&"hello".to_string()));
882        assert!(!map.contains_key(&"world".to_string()));
883    }
884
885    // ----- BoundedQueue -----
886
887    #[test]
888    fn test_queue_basic() {
889        let q: BoundedQueue<i32> = BoundedQueue::new(4);
890        assert!(q.is_empty());
891        assert_eq!(q.capacity(), 4);
892
893        q.push(1).expect("push 1");
894        q.push(2).expect("push 2");
895        assert_eq!(q.len(), 2);
896
897        assert_eq!(q.pop(), Some(1));
898        assert_eq!(q.pop(), Some(2));
899        assert_eq!(q.pop(), None);
900    }
901
902    #[test]
903    fn test_queue_full() {
904        let q: BoundedQueue<i32> = BoundedQueue::new(2);
905        q.push(1).expect("push");
906        q.push(2).expect("push");
907        assert!(q.push(3).is_err()); // full
908    }
909
910    #[test]
911    fn test_queue_concurrent() {
912        let q = Arc::new(BoundedQueue::<u32>::new(1024));
913        let mut handles = Vec::new();
914
915        // Producers
916        for t in 0..4 {
917            let q2 = q.clone();
918            handles.push(thread::spawn(move || {
919                for i in 0..250 {
920                    let val = t * 250 + i;
921                    while q2.push(val).is_err() {
922                        thread::yield_now();
923                    }
924                }
925            }));
926        }
927
928        // Consumer
929        let q3 = q.clone();
930        let consumer = thread::spawn(move || {
931            let mut count = 0u32;
932            while count < 1000 {
933                if q3.pop().is_some() {
934                    count += 1;
935                } else {
936                    thread::yield_now();
937                }
938            }
939            count
940        });
941
942        for h in handles {
943            h.join().expect("producer");
944        }
945        let total = consumer.join().expect("consumer");
946        assert_eq!(total, 1000);
947    }
948
949    #[test]
950    fn test_queue_close() {
951        let q: BoundedQueue<i32> = BoundedQueue::new(8);
952        q.push(1).expect("push");
953        q.close();
954        assert!(q.is_closed());
955        assert!(q.push(2).is_err()); // closed
956        assert_eq!(q.pop(), Some(1)); // can still drain
957    }
958
959    // ----- ConcurrentAccumulator -----
960
961    #[test]
962    fn test_accumulator_sum() {
963        let acc = ConcurrentAccumulator::new(0i64, |a, b| a + b, 4);
964        for i in 1..=100 {
965            acc.accumulate(i);
966        }
967        assert_eq!(acc.result(), 5050);
968    }
969
970    #[test]
971    fn test_accumulator_concurrent() {
972        let acc = Arc::new(ConcurrentAccumulator::new(0u64, |a, b| a + b, 8));
973        let mut handles = Vec::new();
974
975        for _ in 0..8 {
976            let a = acc.clone();
977            handles.push(thread::spawn(move || {
978                for i in 0..1000u64 {
979                    a.accumulate(i);
980                }
981            }));
982        }
983
984        for h in handles {
985            h.join().expect("thread");
986        }
987
988        // Each thread sums 0..999 = 499500; 8 threads = 3996000
989        assert_eq!(acc.result(), 8 * 499500);
990    }
991
992    #[test]
993    fn test_accumulator_reset() {
994        let acc = ConcurrentAccumulator::new(0i32, |a, b| a + b, 4);
995        acc.accumulate(10);
996        acc.accumulate(20);
997        assert_eq!(acc.result(), 30);
998        acc.reset();
999        assert_eq!(acc.result(), 0);
1000    }
1001
1002    // ----- AtomicF64Accumulator -----
1003
1004    #[test]
1005    fn test_atomic_f64() {
1006        let acc = AtomicF64Accumulator::new();
1007        acc.add(1.0);
1008        acc.add(2.0);
1009        acc.add(3.0);
1010        assert!((acc.value() - 6.0).abs() < 1e-10);
1011        assert_eq!(acc.count(), 3);
1012    }
1013
1014    #[test]
1015    fn test_atomic_f64_concurrent() {
1016        let acc = Arc::new(AtomicF64Accumulator::new());
1017        let mut handles = Vec::new();
1018
1019        for _ in 0..8 {
1020            let a = acc.clone();
1021            handles.push(thread::spawn(move || {
1022                for _ in 0..10000 {
1023                    a.add(1.0);
1024                }
1025            }));
1026        }
1027        for h in handles {
1028            h.join().expect("thread");
1029        }
1030        assert!((acc.value() - 80000.0).abs() < 1.0);
1031        assert_eq!(acc.count(), 80000);
1032    }
1033
1034    #[test]
1035    fn test_atomic_f64_reset() {
1036        let acc = AtomicF64Accumulator::new();
1037        acc.add(42.0);
1038        acc.reset();
1039        assert!((acc.value()).abs() < 1e-15);
1040        assert_eq!(acc.count(), 0);
1041    }
1042
1043    // ----- WriterPreferenceRwLock -----
1044
1045    #[test]
1046    fn test_rwlock_basic() {
1047        let lock = WriterPreferenceRwLock::new(42);
1048        {
1049            let r = lock.read().expect("read");
1050            assert_eq!(*r, 42);
1051        }
1052        {
1053            let mut w = lock.write().expect("write");
1054            *w = 99;
1055        }
1056        {
1057            let r = lock.read().expect("read");
1058            assert_eq!(*r, 99);
1059        }
1060    }
1061
1062    #[test]
1063    fn test_rwlock_concurrent_readers() {
1064        let lock = Arc::new(WriterPreferenceRwLock::new(vec![1, 2, 3]));
1065        let mut handles = Vec::new();
1066
1067        for _ in 0..8 {
1068            let l = lock.clone();
1069            handles.push(thread::spawn(move || {
1070                for _ in 0..100 {
1071                    let r = l.read().expect("read");
1072                    assert!(!r.is_empty());
1073                }
1074            }));
1075        }
1076
1077        for h in handles {
1078            h.join().expect("thread");
1079        }
1080    }
1081
1082    #[test]
1083    fn test_rwlock_try_read_write() {
1084        let lock = WriterPreferenceRwLock::new(0);
1085        let r = lock.try_read().expect("try_read");
1086        assert!(r.is_some());
1087        drop(r);
1088
1089        let w = lock.try_write().expect("try_write");
1090        assert!(w.is_some());
1091    }
1092
1093    // ----- DoubleBuffer -----
1094
1095    #[test]
1096    fn test_double_buffer_basic() {
1097        let db = DoubleBuffer::new(0i32);
1098        assert_eq!(db.read_front().expect("read"), 0);
1099
1100        db.publish(42).expect("publish");
1101        assert_eq!(db.read_front().expect("read"), 42);
1102    }
1103
1104    #[test]
1105    fn test_double_buffer_write_and_swap() {
1106        let db = DoubleBuffer::new(vec![0u8; 4]);
1107        db.write_and_swap(|buf| {
1108            buf[0] = 1;
1109            buf[1] = 2;
1110        })
1111        .expect("write");
1112        let front = db.read_front().expect("read");
1113        assert_eq!(front[0], 1);
1114        assert_eq!(front[1], 2);
1115    }
1116
1117    #[test]
1118    fn test_double_buffer_wait_timeout() {
1119        let db = Arc::new(DoubleBuffer::new(0));
1120        let db2 = db.clone();
1121
1122        let producer = thread::spawn(move || {
1123            thread::sleep(Duration::from_millis(50));
1124            db2.publish(99).expect("publish");
1125        });
1126
1127        let result = db.wait_and_read(Duration::from_secs(2)).expect("wait");
1128        assert_eq!(result, Some(99));
1129        producer.join().expect("producer");
1130    }
1131
1132    #[test]
1133    fn test_double_buffer_concurrent() {
1134        let db = Arc::new(DoubleBuffer::new(0u64));
1135        let db_w = db.clone();
1136        let writer_done = Arc::new(AtomicBool::new(false));
1137        let writer_done_w = writer_done.clone();
1138
1139        let writer = thread::spawn(move || {
1140            for i in 1..=100u64 {
1141                db_w.publish(i).expect("publish");
1142            }
1143            writer_done_w.store(true, Ordering::Release);
1144        });
1145
1146        // Reader keeps reading until the writer signals completion, then does a
1147        // final sweep to capture the last published value. This avoids a flaky
1148        // fixed iteration count: under heavy system load the writer thread can
1149        // be slow to schedule, and a fixed-count reader may finish before any
1150        // publish is observable.
1151        let db_r = db.clone();
1152        let writer_done_r = writer_done.clone();
1153        let reader = thread::spawn(move || {
1154            let mut max_seen = 0u64;
1155            loop {
1156                let v = db_r.read_front().expect("read");
1157                if v > max_seen {
1158                    max_seen = v;
1159                }
1160                if writer_done_r.load(Ordering::Acquire) {
1161                    // Final reads to catch the last published value.
1162                    for _ in 0..16 {
1163                        let v = db_r.read_front().expect("read");
1164                        if v > max_seen {
1165                            max_seen = v;
1166                        }
1167                    }
1168                    break;
1169                }
1170                thread::yield_now();
1171            }
1172            max_seen
1173        });
1174
1175        writer.join().expect("writer");
1176        let max_seen = reader.join().expect("reader");
1177        assert!(max_seen > 0);
1178        // Writer published values up to 100; reader must have caught the final
1179        // one after the writer-done signal.
1180        assert_eq!(max_seen, 100);
1181    }
1182}
1183
1184// ---------------------------------------------------------------------------
1185// Lock-free data structure submodules
1186// ---------------------------------------------------------------------------
1187
1188pub mod compressed_trie;
1189pub mod persistent_vector;
1190pub mod queue;
1191pub mod skip_list;
1192pub mod stack;
1193
1194pub use compressed_trie::CompressedTrie;
1195pub use persistent_vector::PersistentRrbVec;
1196pub use queue::LockFreeQueue;
1197pub use skip_list::SkipList;
1198pub use stack::LockFreeStack;
1199
1200// ---------------------------------------------------------------------------
1201// Advanced concurrency submodules
1202// ---------------------------------------------------------------------------
1203
1204pub mod async_utils;
1205pub mod barrier;
1206pub mod parallel_iter;
1207pub mod work_stealing;
1208pub use async_utils as concurrent_async;
1209
1210pub use barrier::{CountDownLatch, CyclicBarrier, PhaseBarrier, SpinBarrier};
1211pub use concurrent_async::{
1212    BackoffStrategy, FutureExecutor, JoinFuture, RetryPolicy, Semaphore, SemaphoreGuard,
1213    TokenBucketRateLimiter,
1214};
1215pub use parallel_iter::{
1216    parallel_filter, parallel_for_each, parallel_map, parallel_merge_sort, parallel_partition,
1217    parallel_prefix_sum, parallel_reduce, parallel_scan, ScanMode,
1218};
1219pub use work_stealing::{
1220    Priority, PriorityTaskQueue, SchedulerConfig, SchedulerStats, StealResult, WorkStealingDeque,
1221    WorkStealingScheduler,
1222};