thread_share/
locked.rs

1//! # Locked Module - ArcThreadShareLocked<T>
2//!
3//! This module provides `ArcThreadShareLocked<T>`, a safe zero-copy structure for
4//! data sharing between threads using read/write locks.
5//!
6//! ## 🚀 Overview
7//!
8//! `ArcThreadShareLocked<T>` is the **recommended alternative** to `ArcThreadShare<T>`
9//! when you need zero-copy operations with guaranteed thread safety. It provides
10//! the performance benefits of zero-copy while maintaining the safety guarantees
11//! of lock-based synchronization.
12//!
13//! ## Key Features
14//!
15//! - **Zero-Copy Operations**: No data cloning during access
16//! - **Guaranteed Thread Safety**: Uses `RwLock` for safe concurrent access
17//! - **High Performance**: Efficient `parking_lot` synchronization primitives
18//! - **Memory Efficiency**: Single copy of data shared across threads
19//! - **No Lost Updates**: All operations are guaranteed to succeed
20//! - **Predictable Behavior**: Consistent performance under all contention levels
21//!
22//! ## When to Use ArcThreadShareLocked<T>
23//!
24//! ### ✅ Perfect Use Cases
25//! - **Safe zero-copy operations** without the limitations of `ArcThreadShare<T>`
26//! - **High-frequency updates** where `ArcThreadShare<T>` would lose operations
27//! - **Critical data integrity** requirements
28//! - **Predictable performance** needs
29//! - **Large data structures** where cloning would be expensive
30//! - **Production applications** requiring reliability
31//!
32//! ### 🔄 Comparison with Other Patterns
33//!
34//! | Pattern | Zero-Copy | Thread Safety | Performance | Reliability |
35//! |---------|-----------|---------------|-------------|-------------|
36//! | **ThreadShare<T>** | ❌ | ✅ | Medium | ✅ |
37//! | **ArcThreadShare<T>** | ✅ | ⚠️ | High (unreliable) | ❌ |
38//! | **ArcThreadShareLocked<T>** | ✅ | ✅ | High | ✅ |
39//!
40//! ## Example Usage
41//!
42//! ### Basic Operations
43//! ```rust
44//! use thread_share::ArcThreadShareLocked;
45//!
46//! let counter = ArcThreadShareLocked::new(0);
47//!
48//! // Safe zero-copy operations
49//! counter.update(|x| *x += 1);
50//! counter.update(|x| *x += 2);
51//!
52//! assert_eq!(counter.get(), 3);
53//! ```
54//!
55//! ### From ThreadShare
56//! ```rust
57//! use thread_share::{share, ArcThreadShareLocked};
58//!
59//! let data = share!(String::from("Hello"));
60//! let arc_data = data.as_arc_locked();
61//! let locked_share = ArcThreadShareLocked::from_arc(arc_data);
62//!
63//! // Safe zero-copy with guaranteed thread safety
64//! locked_share.update(|s| s.push_str(" World"));
65//! ```
66//!
67//! ### High-Frequency Updates
68//! ```rust
69//! use thread_share::ArcThreadShareLocked;
70//! use std::thread;
71//!
72//! let counter = ArcThreadShareLocked::new(0);
73//! let clone = counter.clone();
74//!
75//! // Spawn multiple threads for high-frequency updates
76//! let handles: Vec<_> = (0..10).map(|_| {
77//!     let counter_clone = clone.clone();
78//!     thread::spawn(move || {
79//!         for _ in 0..10000 {
80//!             counter_clone.update(|x| *x += 1);
81//!         }
82//!     })
83//! }).collect();
84//!
85//! // Wait for all threads
86//! for handle in handles {
87//!     handle.join().unwrap();
88//! }
89//!
90//! // All updates are guaranteed to succeed
91//! assert_eq!(counter.get(), 100000);
92//! ```
93//!
94//! ## Performance Characteristics
95//!
96//! - **Low Contention**: Excellent performance, minimal overhead
97//! - **Medium Contention**: Good performance with some lock contention
98//! - **High Contention**: Consistent performance, no lost operations
99//! - **Memory Usage**: Minimal overhead from lock structures
100//! - **Scalability**: Scales well with thread count
101//!
102//! ## Thread Safety
103//!
104//! `ArcThreadShareLocked<T>` automatically implements `Send` and `Sync` traits,
105//! making it safe to use across thread boundaries. The internal `RwLock` ensures
106//! that all operations are thread-safe and no data races can occur.
107//!
108//! ## Memory Management
109//!
110//! - **Arc**: Provides reference counting for shared ownership
111//! - **RwLock**: Ensures exclusive write access and concurrent read access
112//! - **No Box Allocation**: Unlike `ArcThreadShare<T>`, no per-operation allocations
113//! - **Efficient Locking**: Uses `parking_lot` for optimal performance
114//!
115//! ## Best Practices
116//!
117//! 1. **Use for zero-copy needs**: When you need to avoid cloning data
118//! 2. **Prefer over ArcThreadShare**: For reliable, production applications
119//! 3. **Monitor lock contention**: Use `read()` and `write()` methods appropriately
120//! 4. **Keep critical sections short**: Minimize time spent holding locks
121//! 5. **Use descriptive variable names**: Make it clear this is the locked version
122//!
123//! ## Migration from ArcThreadShare<T>
124//!
125//! If you're currently using `ArcThreadShare<T>` and experiencing issues:
126//!
127//! ```rust
128//! // Old: Unreliable atomic operations
129//! use thread_share::ArcThreadShare;
130//! let arc_share = ArcThreadShare::new(0);
131//! arc_share.update(|x| *x += 1); // May fail under contention
132//!
133//! // New: Reliable locked operations
134//! use thread_share::ArcThreadShareLocked;
135//! let locked_share = ArcThreadShareLocked::new(0);
136//! locked_share.update(|x| *x += 1); // Always succeeds
137//! ```
138//!
139//! ## Error Handling
140//!
141//! Unlike `ArcThreadShare<T>`, `ArcThreadShareLocked<T>` operations never fail
142//! due to contention. All operations are guaranteed to complete successfully,
143//! making error handling much simpler.
144//!
145//! ## Integration with ThreadShare
146//!
147//! `ArcThreadShareLocked<T>` integrates seamlessly with `ThreadShare<T>`:
148//!
149//! ```rust
150//! use thread_share::{share, ArcThreadShareLocked};
151//!
152//! let data = share!(vec![1, 2, 3]);
153//!
154//! // Get locked version for zero-copy operations
155//! let arc_data = data.as_arc_locked();
156//! let locked_share = ArcThreadShareLocked::from_arc(arc_data);
157//!
158//! // Use locked version in threads
159//! locked_share.update(|v| v.push(4));
160//!
161//! // Changes are visible in original ThreadShare
162//! assert_eq!(data.get(), vec![1, 2, 3, 4]);
163//! ```
164
165use parking_lot::RwLock;
166use std::sync::Arc;
167
168/// Helper structure for working with Arc<RwLock<T>> directly (with locks)
169///
170/// `ArcThreadShareLocked<T>` is the **recommended alternative** to `ArcThreadShare<T>`
171/// when you need zero-copy operations with guaranteed thread safety. It provides
172/// the performance benefits of zero-copy while maintaining the safety guarantees
173/// of lock-based synchronization.
174///
175/// ## Key Features
176///
177/// - **Zero-Copy Operations**: No data cloning during access
178/// - **Guaranteed Thread Safety**: Uses `RwLock` for safe concurrent access
179/// - **High Performance**: Efficient `parking_lot` synchronization primitives
180/// - **Memory Efficiency**: Single copy of data shared across threads
181/// - **No Lost Updates**: All operations are guaranteed to succeed
182/// - **Predictable Behavior**: Consistent performance under all contention levels
183///
184/// ## When to Use
185///
186/// - **Safe zero-copy operations** without the limitations of `ArcThreadShare<T>`
187/// - **High-frequency updates** where `ArcThreadShare<T>` would lose operations
188/// - **Critical data integrity** requirements
189/// - **Predictable performance** needs
190/// - **Production applications** requiring reliability
191///
192/// ## Example
193///
194/// ```rust
195/// use thread_share::ArcThreadShareLocked;
196///
197/// let counter = ArcThreadShareLocked::new(0);
198///
199/// // Safe zero-copy operations
200/// counter.update(|x| *x += 1);
201/// counter.update(|x| *x += 2);
202///
203/// assert_eq!(counter.get(), 3);
204/// ```
205///
206/// ## Performance
207///
208/// - **Low Contention**: Excellent performance, minimal overhead
209/// - **Medium Contention**: Good performance with some lock contention
210/// - **High Contention**: Consistent performance, no lost operations
211/// - **Memory Usage**: Minimal overhead from lock structures
212/// - **Scalability**: Scales well with thread count
213pub struct ArcThreadShareLocked<T> {
214    pub data: Arc<RwLock<T>>,
215}
216
217// Automatically implement Send and Sync for ArcThreadShareLocked
218unsafe impl<T> Send for ArcThreadShareLocked<T> {}
219unsafe impl<T> Sync for ArcThreadShareLocked<T> {}
220
221impl<T> Clone for ArcThreadShareLocked<T> {
222    fn clone(&self) -> Self {
223        Self {
224            data: Arc::clone(&self.data),
225        }
226    }
227}
228
229impl<T> ArcThreadShareLocked<T> {
230    /// Creates a new ArcThreadShareLocked with data
231    ///
232    /// This method creates a new `ArcThreadShareLocked<T>` instance with the provided data.
233    /// The data is wrapped in an `Arc<RwLock<T>>` for thread-safe sharing.
234    ///
235    /// ## Arguments
236    ///
237    /// * `data` - The initial data to share between threads
238    ///
239    /// ## Returns
240    ///
241    /// A new `ArcThreadShareLocked<T>` instance containing the data.
242    ///
243    /// ## Example
244    ///
245    /// ```rust
246    /// use thread_share::ArcThreadShareLocked;
247    ///
248    /// let counter = ArcThreadShareLocked::new(0);
249    /// let message = ArcThreadShareLocked::new(String::from("Hello"));
250    /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
251    /// ```
252    pub fn new(data: T) -> Self {
253        let arc = Arc::new(RwLock::new(data));
254        Self { data: arc }
255    }
256
257    /// Creates from Arc<RwLock<T>>
258    ///
259    /// This method creates an `ArcThreadShareLocked<T>` from an existing `Arc<RwLock<T>>`.
260    /// Useful when you already have locked data from other sources, such as
261    /// from `ThreadShare<T>::as_arc_locked()`.
262    ///
263    /// ## Arguments
264    ///
265    /// * `arc` - An `Arc<RwLock<T>>` containing the data to share
266    ///
267    /// ## Returns
268    ///
269    /// A new `ArcThreadShareLocked<T>` instance sharing the same data.
270    ///
271    /// ## Example
272    ///
273    /// ```rust
274    /// use thread_share::{share, ArcThreadShareLocked};
275    ///
276    /// let data = share!(vec![1, 2, 3]);
277    /// let arc_data = data.as_arc_locked();
278    /// let locked_share = ArcThreadShareLocked::from_arc(arc_data);
279    ///
280    /// // Now you can use safe zero-copy operations
281    /// locked_share.update(|v| v.push(4));
282    /// ```
283    pub fn from_arc(arc: Arc<RwLock<T>>) -> Self {
284        Self { data: arc }
285    }
286
287    /// Gets a copy of data
288    ///
289    /// This method retrieves a copy of the current data. The operation is safe
290    /// but involves cloning the data.
291    ///
292    /// ## Requirements
293    ///
294    /// The type `T` must implement `Clone` trait.
295    ///
296    /// ## Returns
297    ///
298    /// A copy of the current data.
299    ///
300    /// ## Example
301    ///
302    /// ```rust
303    /// use thread_share::ArcThreadShareLocked;
304    ///
305    /// let counter = ArcThreadShareLocked::new(42);
306    /// let value = counter.get();
307    /// assert_eq!(value, 42);
308    /// ```
309    pub fn get(&self) -> T
310    where
311        T: Clone,
312    {
313        self.data.read().clone()
314    }
315
316    /// Gets a reference to data (no cloning!)
317    ///
318    /// This method provides read-only access to the data without cloning.
319    /// The returned guard holds the read lock until it's dropped.
320    ///
321    /// ## Returns
322    ///
323    /// A read guard that provides access to the data.
324    ///
325    /// ## Example
326    ///
327    /// ```rust
328    /// use thread_share::ArcThreadShareLocked;
329    ///
330    /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
331    ///
332    /// // Get reference without cloning
333    /// {
334    ///     let guard = data.get_ref();
335    ///     assert_eq!(guard.len(), 3);
336    ///     assert_eq!(guard[0], 1);
337    ///     // Guard is automatically dropped here, releasing the lock
338    /// }
339    /// ```
340    ///
341    /// ## Note
342    ///
343    /// This method will block until the read lock can be acquired.
344    /// Multiple threads can read simultaneously.
345    /// For non-blocking behavior, use `try_get_ref()`.
346    pub fn get_ref(&self) -> parking_lot::RwLockReadGuard<'_, T> {
347        self.data.read()
348    }
349
350    /// Tries to get a reference to data without blocking
351    ///
352    /// This method attempts to acquire a read lock without blocking.
353    /// Returns `None` if the lock cannot be acquired immediately.
354    ///
355    /// ## Returns
356    ///
357    /// `Some(guard)` if the lock was acquired, `None` if it couldn't be acquired.
358    ///
359    /// ## Example
360    ///
361    /// ```rust
362    /// use thread_share::ArcThreadShareLocked;
363    ///
364    /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
365    ///
366    /// // Try to get reference without blocking
367    /// if let Some(guard) = data.try_get_ref() {
368    ///     assert_eq!(guard.len(), 3);
369    ///     assert_eq!(guard[0], 1);
370    ///     // Guard is automatically dropped here
371    /// } else {
372    ///     // Lock was not available
373    /// }
374    ///
375    /// // Ensure data is still accessible
376    /// assert_eq!(data.get(), vec![1, 2, 3]);
377    /// ```
378    pub fn try_get_ref(&self) -> Option<parking_lot::RwLockReadGuard<'_, T>> {
379        self.data.try_read()
380    }
381
382    /// Gets a mutable reference to data (no cloning!)
383    ///
384    /// This method provides mutable access to the data without cloning.
385    /// The returned guard holds the write lock until it's dropped.
386    ///
387    /// ## Returns
388    ///
389    /// A write guard that provides mutable access to the data.
390    ///
391    /// ## Example
392    ///
393    /// ```rust
394    /// use thread_share::ArcThreadShareLocked;
395    ///
396    /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
397    ///
398    /// // Get mutable reference without cloning
399    /// {
400    ///     let mut guard = data.get_mut();
401    ///     guard.push(4);
402    ///     // Guard is automatically dropped here, releasing the lock
403    /// }
404    ///
405    /// assert_eq!(data.get(), vec![1, 2, 3, 4]);
406    /// ```
407    ///
408    /// ## Warning
409    ///
410    /// This method will block until the write lock can be acquired.
411    /// In high-contention scenarios, this can cause delays.
412    /// For non-blocking behavior, use `try_get_mut()`.
413    ///
414    /// ## Best Practices
415    ///
416    /// - Keep critical sections short to minimize lock contention
417    /// - Always drop the guard explicitly in complex scenarios
418    /// - Consider using `try_get_mut()` for non-blocking operations
419    pub fn get_mut(&self) -> parking_lot::RwLockWriteGuard<'_, T> {
420        self.data.write()
421    }
422
423    /// Tries to get a mutable reference to data without blocking
424    ///
425    /// This method attempts to acquire a write lock without blocking.
426    /// Returns `None` if the lock cannot be acquired immediately.
427    ///
428    /// ## Returns
429    ///
430    /// `Some(guard)` if the lock was acquired, `None` if it couldn't be acquired.
431    ///
432    /// ## Example
433    ///
434    /// ```rust
435    /// use thread_share::ArcThreadShareLocked;
436    ///
437    /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
438    ///
439    /// // Try to get mutable reference without blocking
440    /// if let Some(mut guard) = data.try_get_mut() {
441    ///     guard.push(4);
442    ///     // Guard is automatically dropped here
443    /// }
444    ///
445    /// // Ensure data is still accessible
446    /// assert_eq!(data.get(), vec![1, 2, 3, 4]);
447    /// ```
448    pub fn try_get_mut(&self) -> Option<parking_lot::RwLockWriteGuard<'_, T>> {
449        self.data.try_write()
450    }
451
452    /// Sets data
453    ///
454    /// This method replaces the current data with new data.
455    ///
456    /// ## Arguments
457    ///
458    /// * `new_data` - The new data to set
459    ///
460    /// ## Example
461    ///
462    /// ```rust
463    /// use thread_share::ArcThreadShareLocked;
464    ///
465    /// let counter = ArcThreadShareLocked::new(0);
466    /// counter.set(100);
467    /// assert_eq!(counter.get(), 100);
468    /// ```
469    pub fn set(&self, new_data: T) {
470        let mut data = self.data.write();
471        *data = new_data;
472    }
473
474    /// Updates data using a function
475    ///
476    /// This method allows you to modify the data through a closure.
477    /// The operation is guaranteed to succeed and is thread-safe.
478    ///
479    /// ## Arguments
480    ///
481    /// * `f` - Closure that receives a mutable reference to the data
482    ///
483    /// ## Example
484    ///
485    /// ```rust
486    /// use thread_share::ArcThreadShareLocked;
487    ///
488    /// let counter = ArcThreadShareLocked::new(0);
489    ///
490    /// counter.update(|x| *x += 1);
491    /// assert_eq!(counter.get(), 1);
492    ///
493    /// counter.update(|x| *x *= 2);
494    /// assert_eq!(counter.get(), 2);
495    /// ```
496    pub fn update<F>(&self, f: F)
497    where
498        F: FnOnce(&mut T),
499    {
500        let mut data = self.data.write();
501        f(&mut data);
502    }
503
504    /// Reads data through a function
505    ///
506    /// This method provides read-only access to the data through a closure.
507    /// Multiple threads can read simultaneously.
508    ///
509    /// ## Arguments
510    ///
511    /// * `f` - Closure that receives a reference to the data
512    ///
513    /// ## Returns
514    ///
515    /// The result of the closure execution.
516    ///
517    /// ## Example
518    ///
519    /// ```rust
520    /// use thread_share::ArcThreadShareLocked;
521    ///
522    /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
523    ///
524    /// let length = data.read(|v| v.len());
525    /// assert_eq!(length, 3);
526    ///
527    /// let sum: i32 = data.read(|v| v.iter().sum());
528    /// assert_eq!(sum, 6);
529    /// ```
530    pub fn read<F, R>(&self, f: F) -> R
531    where
532        F: FnOnce(&T) -> R,
533    {
534        let data = self.data.read();
535        f(&data)
536    }
537
538    /// Writes data through a function
539    ///
540    /// This method provides mutable access to the data through a closure.
541    /// Only one thread can write at a time.
542    ///
543    /// ## Arguments
544    ///
545    /// * `f` - Closure that receives a mutable reference to the data
546    ///
547    /// ## Returns
548    ///
549    /// The result of the closure execution.
550    ///
551    /// ## Example
552    ///
553    /// ```rust
554    /// use thread_share::ArcThreadShareLocked;
555    ///
556    /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
557    ///
558    /// let length = data.write(|v| {
559    ///     v.push(4);
560    ///     v.len()
561    /// });
562    ///
563    /// assert_eq!(length, 4);
564    /// assert_eq!(data.get(), vec![1, 2, 3, 4]);
565    /// ```
566    pub fn write<F, R>(&self, f: F) -> R
567    where
568        F: FnOnce(&mut T) -> R,
569    {
570        let mut data = self.data.write();
571        f(&mut data)
572    }
573}