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#[cfg(feature = "serialize")]
169use serde::{de::DeserializeOwned};
170
171/// Helper structure for working with Arc<RwLock<T>> directly (with locks)
172///
173/// `ArcThreadShareLocked<T>` is the **recommended alternative** to `ArcThreadShare<T>`
174/// when you need zero-copy operations with guaranteed thread safety. It provides
175/// the performance benefits of zero-copy while maintaining the safety guarantees
176/// of lock-based synchronization.
177///
178/// ## Key Features
179///
180/// - **Zero-Copy Operations**: No data cloning during access
181/// - **Guaranteed Thread Safety**: Uses `RwLock` for safe concurrent access
182/// - **High Performance**: Efficient `parking_lot` synchronization primitives
183/// - **Memory Efficiency**: Single copy of data shared across threads
184/// - **No Lost Updates**: All operations are guaranteed to succeed
185/// - **Predictable Behavior**: Consistent performance under all contention levels
186///
187/// ## When to Use
188///
189/// - **Safe zero-copy operations** without the limitations of `ArcThreadShare<T>`
190/// - **High-frequency updates** where `ArcThreadShare<T>` would lose operations
191/// - **Critical data integrity** requirements
192/// - **Predictable performance** needs
193/// - **Production applications** requiring reliability
194///
195/// ## Example
196///
197/// ```rust
198/// use thread_share::ArcThreadShareLocked;
199///
200/// let counter = ArcThreadShareLocked::new(0);
201///
202/// // Safe zero-copy operations
203/// counter.update(|x| *x += 1);
204/// counter.update(|x| *x += 2);
205///
206/// assert_eq!(counter.get(), 3);
207/// ```
208///
209/// ## Performance
210///
211/// - **Low Contention**: Excellent performance, minimal overhead
212/// - **Medium Contention**: Good performance with some lock contention
213/// - **High Contention**: Consistent performance, no lost operations
214/// - **Memory Usage**: Minimal overhead from lock structures
215/// - **Scalability**: Scales well with thread count
216pub struct ArcThreadShareLocked<T> {
217 pub data: Arc<RwLock<T>>,
218}
219
220// Automatically implement Send and Sync for ArcThreadShareLocked
221unsafe impl<T> Send for ArcThreadShareLocked<T> {}
222unsafe impl<T> Sync for ArcThreadShareLocked<T> {}
223
224impl<T> Clone for ArcThreadShareLocked<T> {
225 fn clone(&self) -> Self {
226 Self {
227 data: Arc::clone(&self.data),
228 }
229 }
230}
231
232impl<T> ArcThreadShareLocked<T> {
233 /// Creates a new ArcThreadShareLocked with data
234 ///
235 /// This method creates a new `ArcThreadShareLocked<T>` instance with the provided data.
236 /// The data is wrapped in an `Arc<RwLock<T>>` for thread-safe sharing.
237 ///
238 /// ## Arguments
239 ///
240 /// * `data` - The initial data to share between threads
241 ///
242 /// ## Returns
243 ///
244 /// A new `ArcThreadShareLocked<T>` instance containing the data.
245 ///
246 /// ## Example
247 ///
248 /// ```rust
249 /// use thread_share::ArcThreadShareLocked;
250 ///
251 /// let counter = ArcThreadShareLocked::new(0);
252 /// let message = ArcThreadShareLocked::new(String::from("Hello"));
253 /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
254 /// ```
255 pub fn new(data: T) -> Self {
256 let arc = Arc::new(RwLock::new(data));
257 Self { data: arc }
258 }
259
260 /// Creates from Arc<RwLock<T>>
261 ///
262 /// This method creates an `ArcThreadShareLocked<T>` from an existing `Arc<RwLock<T>>`.
263 /// Useful when you already have locked data from other sources, such as
264 /// from `ThreadShare<T>::as_arc_locked()`.
265 ///
266 /// ## Arguments
267 ///
268 /// * `arc` - An `Arc<RwLock<T>>` containing the data to share
269 ///
270 /// ## Returns
271 ///
272 /// A new `ArcThreadShareLocked<T>` instance sharing the same data.
273 ///
274 /// ## Example
275 ///
276 /// ```rust
277 /// use thread_share::{share, ArcThreadShareLocked};
278 ///
279 /// let data = share!(vec![1, 2, 3]);
280 /// let arc_data = data.as_arc_locked();
281 /// let locked_share = ArcThreadShareLocked::from_arc(arc_data);
282 ///
283 /// // Now you can use safe zero-copy operations
284 /// locked_share.update(|v| v.push(4));
285 /// ```
286 pub fn from_arc(arc: Arc<RwLock<T>>) -> Self {
287 Self { data: arc }
288 }
289
290 /// Gets a copy of data
291 ///
292 /// This method retrieves a copy of the current data. The operation is safe
293 /// but involves cloning the data.
294 ///
295 /// ## Requirements
296 ///
297 /// The type `T` must implement `Clone` trait.
298 ///
299 /// ## Returns
300 ///
301 /// A copy of the current data.
302 ///
303 /// ## Example
304 ///
305 /// ```rust
306 /// use thread_share::ArcThreadShareLocked;
307 ///
308 /// let counter = ArcThreadShareLocked::new(42);
309 /// let value = counter.get();
310 /// assert_eq!(value, 42);
311 /// ```
312 pub fn get(&self) -> T
313 where
314 T: Clone,
315 {
316 self.data.read().clone()
317 }
318
319 /// Gets a reference to data (no cloning!)
320 ///
321 /// This method provides read-only access to the data without cloning.
322 /// The returned guard holds the read lock until it's dropped.
323 ///
324 /// ## Returns
325 ///
326 /// A read guard that provides access to the data.
327 ///
328 /// ## Example
329 ///
330 /// ```rust
331 /// use thread_share::ArcThreadShareLocked;
332 ///
333 /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
334 ///
335 /// // Get reference without cloning
336 /// {
337 /// let guard = data.get_ref();
338 /// assert_eq!(guard.len(), 3);
339 /// assert_eq!(guard[0], 1);
340 /// // Guard is automatically dropped here, releasing the lock
341 /// }
342 /// ```
343 ///
344 /// ## Note
345 ///
346 /// This method will block until the read lock can be acquired.
347 /// Multiple threads can read simultaneously.
348 /// For non-blocking behavior, use `try_get_ref()`.
349 pub fn get_ref(&self) -> parking_lot::RwLockReadGuard<'_, T> {
350 self.data.read()
351 }
352
353 /// Tries to get a reference to data without blocking
354 ///
355 /// This method attempts to acquire a read lock without blocking.
356 /// Returns `None` if the lock cannot be acquired immediately.
357 ///
358 /// ## Returns
359 ///
360 /// `Some(guard)` if the lock was acquired, `None` if it couldn't be acquired.
361 ///
362 /// ## Example
363 ///
364 /// ```rust
365 /// use thread_share::ArcThreadShareLocked;
366 ///
367 /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
368 ///
369 /// // Try to get reference without blocking
370 /// if let Some(guard) = data.try_get_ref() {
371 /// assert_eq!(guard.len(), 3);
372 /// assert_eq!(guard[0], 1);
373 /// // Guard is automatically dropped here
374 /// } else {
375 /// // Lock was not available
376 /// }
377 ///
378 /// // Ensure data is still accessible
379 /// assert_eq!(data.get(), vec![1, 2, 3]);
380 /// ```
381 pub fn try_get_ref(&self) -> Option<parking_lot::RwLockReadGuard<'_, T>> {
382 self.data.try_read()
383 }
384
385 /// Gets a mutable reference to data (no cloning!)
386 ///
387 /// This method provides mutable access to the data without cloning.
388 /// The returned guard holds the write lock until it's dropped.
389 ///
390 /// ## Returns
391 ///
392 /// A write guard that provides mutable access to the data.
393 ///
394 /// ## Example
395 ///
396 /// ```rust
397 /// use thread_share::ArcThreadShareLocked;
398 ///
399 /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
400 ///
401 /// // Get mutable reference without cloning
402 /// {
403 /// let mut guard = data.get_mut();
404 /// guard.push(4);
405 /// // Guard is automatically dropped here, releasing the lock
406 /// }
407 ///
408 /// assert_eq!(data.get(), vec![1, 2, 3, 4]);
409 /// ```
410 ///
411 /// ## Warning
412 ///
413 /// This method will block until the write lock can be acquired.
414 /// In high-contention scenarios, this can cause delays.
415 /// For non-blocking behavior, use `try_get_mut()`.
416 ///
417 /// ## Best Practices
418 ///
419 /// - Keep critical sections short to minimize lock contention
420 /// - Always drop the guard explicitly in complex scenarios
421 /// - Consider using `try_get_mut()` for non-blocking operations
422 pub fn get_mut(&self) -> parking_lot::RwLockWriteGuard<'_, T> {
423 self.data.write()
424 }
425
426 /// Tries to get a mutable reference to data without blocking
427 ///
428 /// This method attempts to acquire a write lock without blocking.
429 /// Returns `None` if the lock cannot be acquired immediately.
430 ///
431 /// ## Returns
432 ///
433 /// `Some(guard)` if the lock was acquired, `None` if it couldn't be acquired.
434 ///
435 /// ## Example
436 ///
437 /// ```rust
438 /// use thread_share::ArcThreadShareLocked;
439 ///
440 /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
441 ///
442 /// // Try to get mutable reference without blocking
443 /// if let Some(mut guard) = data.try_get_mut() {
444 /// guard.push(4);
445 /// // Guard is automatically dropped here
446 /// }
447 ///
448 /// // Ensure data is still accessible
449 /// assert_eq!(data.get(), vec![1, 2, 3, 4]);
450 /// ```
451 pub fn try_get_mut(&self) -> Option<parking_lot::RwLockWriteGuard<'_, T>> {
452 self.data.try_write()
453 }
454
455 /// Sets data
456 ///
457 /// This method replaces the current data with new data.
458 ///
459 /// ## Arguments
460 ///
461 /// * `new_data` - The new data to set
462 ///
463 /// ## Example
464 ///
465 /// ```rust
466 /// use thread_share::ArcThreadShareLocked;
467 ///
468 /// let counter = ArcThreadShareLocked::new(0);
469 /// counter.set(100);
470 /// assert_eq!(counter.get(), 100);
471 /// ```
472 pub fn set(&self, new_data: T) {
473 let mut data = self.data.write();
474 *data = new_data;
475 }
476
477 /// Updates data using a function
478 ///
479 /// This method allows you to modify the data through a closure.
480 /// The operation is guaranteed to succeed and is thread-safe.
481 ///
482 /// ## Arguments
483 ///
484 /// * `f` - Closure that receives a mutable reference to the data
485 ///
486 /// ## Example
487 ///
488 /// ```rust
489 /// use thread_share::ArcThreadShareLocked;
490 ///
491 /// let counter = ArcThreadShareLocked::new(0);
492 ///
493 /// counter.update(|x| *x += 1);
494 /// assert_eq!(counter.get(), 1);
495 ///
496 /// counter.update(|x| *x *= 2);
497 /// assert_eq!(counter.get(), 2);
498 /// ```
499 pub fn update<F>(&self, f: F)
500 where
501 F: FnOnce(&mut T),
502 {
503 let mut data = self.data.write();
504 f(&mut data);
505 }
506
507 /// Reads data through a function
508 ///
509 /// This method provides read-only access to the data through a closure.
510 /// Multiple threads can read simultaneously.
511 ///
512 /// ## Arguments
513 ///
514 /// * `f` - Closure that receives a reference to the data
515 ///
516 /// ## Returns
517 ///
518 /// The result of the closure execution.
519 ///
520 /// ## Example
521 ///
522 /// ```rust
523 /// use thread_share::ArcThreadShareLocked;
524 ///
525 /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
526 ///
527 /// let length = data.read(|v| v.len());
528 /// assert_eq!(length, 3);
529 ///
530 /// let sum: i32 = data.read(|v| v.iter().sum());
531 /// assert_eq!(sum, 6);
532 /// ```
533 pub fn read<F, R>(&self, f: F) -> R
534 where
535 F: FnOnce(&T) -> R,
536 {
537 let data = self.data.read();
538 f(&data)
539 }
540
541 /// Writes data through a function
542 ///
543 /// This method provides mutable access to the data through a closure.
544 /// Only one thread can write at a time.
545 ///
546 /// ## Arguments
547 ///
548 /// * `f` - Closure that receives a mutable reference to the data
549 ///
550 /// ## Returns
551 ///
552 /// The result of the closure execution.
553 ///
554 /// ## Example
555 ///
556 /// ```rust
557 /// use thread_share::ArcThreadShareLocked;
558 ///
559 /// let data = ArcThreadShareLocked::new(vec![1, 2, 3]);
560 ///
561 /// let length = data.write(|v| {
562 /// v.push(4);
563 /// v.len()
564 /// });
565 ///
566 /// assert_eq!(length, 4);
567 /// assert_eq!(data.get(), vec![1, 2, 3, 4]);
568 /// ```
569 pub fn write<F, R>(&self, f: F) -> R
570 where
571 F: FnOnce(&mut T) -> R,
572 {
573 let mut data = self.data.write();
574 f(&mut data)
575 }
576
577 #[cfg(feature = "serialize")]
578 pub fn to_json(&self) -> Result<String, serde_json::Error>
579 where
580 T: serde::Serialize + Clone,
581 {
582 serde_json::to_string(&self.get())
583 }
584
585 #[cfg(feature = "serialize")]
586 pub fn from_json<D: DeserializeOwned>(&self, json: &str) -> Result<D, serde_json::Error> {
587 serde_json::from_str(json)
588 }
589}