sync_cell/
lib.rs

1//! A module containing easier to use thread-safe types for the creation of larger thread safe
2//! systems.
3//!
4//! ## Included Types
5//! - `SyncCell` - A replacement for `std::cell::RefCell` and `std::cell::Cell` with an easier to
6//! use API than `std::sync::RwLock`.
7//! - `HeldSyncCell` - A cell that maintains a previous value until the `update` method is called
8//! at which point any changes to the value are applied.
9
10use std::{sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}, cmp::Ordering, hash::{Hash, Hasher}, mem::swap};
11
12/// A mutable memory location that can be modified safely from multiple threads.
13/// This structure is similar to `std::cell::Cell` or `std::cell::RefCell`
14/// while being thread-safe.
15/// It functions as a thin wrapper around `std::sync::RwLock` while assuming that poisoned locks
16/// indicate an unrecoverable error. This makes it more ergonomic to use than `RwLock` at the cost
17/// of some stability.
18/// 
19/// # As a `Cell` replacement.
20/// `SyncCell` can be used to replace the functionality of a `std::cell::Cell` in contexts where
21/// data need to mutably accessed across multiple threads.
22/// ## Using `std::cell::Cell`
23/// ```
24/// use std::cell::Cell;
25///
26/// let cell = Cell::new(0);
27///
28/// cell.set(1);
29///
30/// println!("{}", cell.get());
31/// ```
32/// ## Using `sync_cell::SyncCell`
33/// ```
34/// use sync_cell::SyncCell;
35///
36/// let cell = SyncCell::new(0);
37///
38/// cell.set(1);
39///
40/// println!("{}", cell.get());
41/// ```
42///
43/// # As a `RefCell` replacement.
44/// `SyncCell` can also be used to replace usages of `RefCell`.
45/// ## Using `std::cell::RefCell`
46/// ```
47/// use std::cell::RefCell;
48///
49/// let cell = RefCell::new((0, 1));
50///
51/// let borrowed = cell.borrow();
52/// println!("{}", borrowed.0);
53/// drop(borrowed);
54///
55/// let mut mutable_borrow = cell.borrow_mut();
56/// mutable_borrow.1 = 2;
57/// drop(mutable_borrow);
58///
59/// let borrowed = cell.borrow();
60/// println!("{:?}", borrowed);
61/// ```
62/// ## Using `sync_cell::SyncCell`
63/// ```
64/// use sync_cell::SyncCell;
65///
66/// let cell = SyncCell::new((0, 1));
67///
68/// let borrowed = cell.borrow();
69/// println!("{}", borrowed.0);
70/// drop(borrowed);
71///
72/// let mut mutable_borrow = cell.borrow_mut();
73/// mutable_borrow.1 = 2;
74/// drop(mutable_borrow);
75///
76/// let borrowed = cell.borrow();
77/// println!("{:?}", borrowed);
78/// ```
79///
80/// # Panicking
81/// Unlike `std::sync::RwLock`, `SyncCell` will panic rather than return an error when the lock
82/// becomes poisoned.
83#[derive(Debug)]
84pub struct SyncCell<T: ?Sized> {
85    /// The internal lock holding the data of this cell.
86    data: RwLock<T>,
87}
88
89impl <T> SyncCell<T> {
90    /// Creates a new `SyncCell`.
91    ///
92    /// - `data` - The initial value of the `SyncCell`.
93    pub const fn new(data: T) -> Self {
94        Self {
95            data: RwLock::new(data)
96        }
97    }
98
99    /// Sets the value contained in this cell.
100    ///
101    /// - `value` - The new value of the cell.
102    ///
103    /// # Panicking
104    /// This method will panic if the lock becomes poisoned.
105    pub fn set(&self, value: T) {
106        match self.data.write() {
107            Ok(mut data) => *data = value,
108            Err(err) => panic!("Failed to set cell value. Lock was poisoned: {}", err),
109        }
110    }
111
112    /// Retrieves the inner value stored in this `SyncCell`. 
113    ///
114    /// # Panicking
115    /// This method will panic if the lock becomes poisoned.
116    pub fn into_inner(self) -> T {
117        match self.data.into_inner() {
118            Ok(data) => data,
119            Err(err) => panic!("Failed to get cell value. Lock was poisoned: {}", err),
120        }
121    }
122
123    /// Replaces the internal value contained in this cell.
124    /// The previous value is returned.
125    ///
126    /// - `value` - The new value of the cell.
127    ///
128    /// # Panicking
129    /// This method will panic if the lock becomes poisoned.
130    pub fn replace(&self, mut value: T) -> T {
131        match self.data.write() {
132            Ok(mut data) => {
133                swap(&mut *data, &mut value);
134                value
135            },
136            Err(err) => panic!("Failed to set cell value. Lock was poisoned: {}", err),
137        }
138    }
139}
140
141impl <T: ?Sized> SyncCell<T> {
142    /// Borrows a immutable reference to the data stored in this cell.
143    ///
144    /// # Panicking
145    /// This method will panic if the lock becomes poisoned.
146    pub fn borrow(&self) -> RwLockReadGuard<T> {
147        match self.data.read() {
148            Ok(data) => data,
149            Err(err) => panic!("Failed to get cell value. Lock was poisoned: {}", err),
150        }
151    }
152    
153    /// Borrows a mutable reference to the data stored in this cell.
154    ///
155    /// # Panicking
156    /// This method will panic if the lock becomes poisoned.
157    pub fn borrow_mut(&self) -> RwLockWriteGuard<T> {
158        match self.data.write() {
159            Ok(data) => data,
160            Err(err) => panic!("Failed to get cell value. Lock was poisoned: {}", err),
161        }
162    }
163}
164
165impl <T: Clone> SyncCell<T> {
166    /// Gets the value contained in this cell.
167    ///
168    /// # Panicking
169    /// This method will panic if the lock becomes poisoned.
170    pub fn get(&self) -> T {
171        match self.data.read() {
172            Ok(data) => data.clone(),
173            Err(err) => panic!("Failed to get cell value. Lock was poisoned: {}", err),
174        }
175    }
176}
177
178impl <T: Clone> Clone for SyncCell<T> {
179    fn clone(&self) -> Self {
180        Self::new(self.get())
181    }
182}
183
184impl <T: Default> Default for SyncCell<T> {
185    fn default() -> Self {
186        Self::new(T::default())
187    }
188}
189
190impl <T: PartialEq + ?Sized> PartialEq for SyncCell<T> {
191    fn eq(&self, other: &Self) -> bool {
192        self.borrow().eq(&*other.borrow())
193    }
194}
195
196impl <T: Eq + ?Sized> Eq for SyncCell<T> {
197}
198
199impl <T: PartialOrd + ?Sized> PartialOrd for SyncCell<T> {
200    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
201        self.borrow().partial_cmp(&*other.borrow())
202    }
203}
204
205impl <T: Ord + ?Sized> Ord for SyncCell<T> {
206    fn cmp(&self, other: &Self) -> Ordering {
207        self.borrow().cmp(&*other.borrow())
208    }
209}
210
211impl <T: Hash + ?Sized> Hash for SyncCell<T> {
212    fn hash<H: Hasher>(&self, state: &mut H) {
213        self.borrow().hash(state)
214    }
215}
216
217impl <T> From<T> for SyncCell<T> {
218    fn from(value: T) -> Self {
219        Self::new(value)
220    }
221}
222
223/// A cell that holds a value until any changes made are applied by use of the `update` method.
224/// Getting the value or obtaining a reference to the value in this cell will return the value
225/// immediately following the last call to `update`. This allows for mutably altering a value while
226/// keeping a reference for a short amount of time to the old value.
227/// This is useful when you want a method in a structure to be able to modify the structure it is
228/// being called from such as when changing the scene in a game engine.
229///
230/// # Usage
231/// ```
232/// use sync_cell::HeldSyncCell;
233///
234/// let cell = HeldSyncCell::new(0);
235///
236/// // Set the next value of the cell.
237/// cell.set(1);
238///
239/// // Cell continues to hold a value of 0 until the `update` method is called.
240/// assert_eq!(0, cell.get());
241///
242/// cell.update();
243/// assert_eq!(1, cell.get());
244/// ```
245pub struct HeldSyncCell<T> {
246    /// The current value that is made available.
247    current_value: SyncCell<T>,
248    /// The value to use next.
249    next_value: SyncCell<Option<T>>,
250}
251
252impl <T> HeldSyncCell<T> {
253    /// Creates a new `HeldSyncCell`.
254    ///
255    /// - `data` - The initial value of the `HeldSyncCell`.
256    pub const fn new(data: T) -> Self {
257        Self {
258            current_value: SyncCell::new(data),
259            next_value: SyncCell::new(None),
260        }
261    }
262
263    /// Sets the value contained in this cell.
264    /// This value will only become available once the `update` method is called.
265    /// 
266    /// In the case that multiple threads call this method simultaniously,
267    /// the order in which the calls are processed is not defined. However, the final result will
268    /// be the value specified by one of the method calls.
269    ///
270    /// - `value` - The new value of the cell.
271    ///
272    /// # Panicking
273    /// This method will panic if any of the locks become poisoned.
274    pub fn set(&self, value: T) {
275        self.next_value.set(Some(value))
276    }
277
278    /// Retrieves the inner value stored in this `HeldSyncCell`. 
279    /// This will return the most up-to-date value even if `update` has not been called.
280    ///
281    /// # Panicking
282    /// This method will panic if any of the locks become poisoned.
283    pub fn into_inner(self) -> T {
284        self.next_value.into_inner()
285            .unwrap_or(self.current_value.into_inner())
286    }
287
288    /// Borrows a immutable reference to the data stored in this cell.
289    /// This is a reference to the current value of the cell.
290    ///
291    /// # Panicking
292    /// This method will panic if any of the locks become poisoned.
293    pub fn borrow(&self) -> RwLockReadGuard<T> {
294        self.current_value.borrow()
295    }
296    
297    /// Borrows a mutable reference to the data stored in this cell.
298    /// This is a reference to the current value of the cell not the incoming value. Any changes to
299    /// the value will update the current value.
300    ///
301    /// # Panicking
302    /// This method will panic if any of the locks become poisoned.
303    pub fn borrow_mut(&self) -> RwLockWriteGuard<T> {
304        self.current_value.borrow_mut()
305    }
306
307    /// Checks if a new nalue is available that can be applied by calling `update`.
308    ///
309    /// # Panicking
310    /// This method will panic if any of the locks become poisoned.
311    pub fn has_update(&self) -> bool {
312        self.next_value.borrow().is_some()
313    }
314
315    /// Updates the internal value of this cell.
316    /// This involves replacing the current value with the incoming value if it is available.
317    ///
318    /// # Panicking
319    /// This method will panic if any of the locks become poisoned.
320    pub fn update(&self) {
321        if let Some(next) = self.next_value.replace(None) {
322            self.current_value.set(next);
323        }
324    } 
325}
326
327impl <T: Clone> HeldSyncCell<T> {
328    /// Gets the value contained in this cell.
329    ///
330    /// # Panicking
331    /// This method will panic if any of the locks become poisoned.
332    pub fn get(&self) -> T {
333        self.current_value.get()
334    }
335}
336
337impl <T: Clone> Clone for HeldSyncCell<T> {
338    fn clone(&self) -> Self {
339        Self::new(self.get())
340    }
341}
342
343impl <T: Default> Default for HeldSyncCell<T> {
344    fn default() -> Self {
345        Self::new(T::default())
346    }
347}
348
349impl <T: PartialEq> PartialEq for HeldSyncCell<T> {
350    fn eq(&self, other: &Self) -> bool {
351        self.borrow().eq(&*other.borrow())
352    }
353}
354
355impl <T: Eq> Eq for HeldSyncCell<T> {
356}
357
358impl <T: PartialOrd> PartialOrd for HeldSyncCell<T> {
359    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
360        self.borrow().partial_cmp(&*other.borrow())
361    }
362}
363
364impl <T: Ord> Ord for HeldSyncCell<T> {
365    fn cmp(&self, other: &Self) -> Ordering {
366        self.borrow().cmp(&*other.borrow())
367    }
368}
369
370impl <T: Hash> Hash for HeldSyncCell<T> {
371    fn hash<H: Hasher>(&self, state: &mut H) {
372        self.borrow().hash(state)
373    }
374}
375
376impl <T> From<T> for HeldSyncCell<T> {
377    fn from(value: T) -> Self {
378        Self::new(value)
379    }
380}
381
382#[cfg(test)]
383mod tests {
384    use core::panic;
385    use std::{thread, sync::Arc};
386
387    use crate::{SyncCell, HeldSyncCell};
388
389    #[test]
390    pub fn test_sync_cell_new() {
391        let _cell = SyncCell::new(1);
392    } 
393
394    #[test]
395    pub fn test_sync_cell_set() {
396        let cell = SyncCell::new(2);
397
398        cell.set(3);
399
400        assert_eq!(3, cell.get())
401    }
402    
403    #[test]
404    pub fn test_sync_cell_get() {
405        let cell = SyncCell::new(4);
406
407        assert_eq!(4, cell.get())
408    }
409    
410    #[test]
411    pub fn test_sync_cell_replace() {
412        let cell = SyncCell::new(2);
413
414        let old = cell.replace(3);
415        
416        assert_eq!(2, old);
417        assert_eq!(3, cell.get())
418    }
419    
420    #[test]
421    #[should_panic]
422    pub fn test_sync_cell_replace_poisoned() {
423        let cell = Arc::new(SyncCell::new(4));
424
425        let cell2 = cell.clone();
426
427        let _ = thread::spawn(move || {
428            let _borrow = cell2.borrow();
429
430            panic!("Intentional panic.");
431        }).join();
432
433        let old = cell.replace(3);
434        
435        assert_ne!(2, old);
436        assert_ne!(3, cell.get())
437    }
438    
439    #[test]
440    pub fn test_sync_cell_into_inner() {
441        let cell = SyncCell::new(4);
442
443        assert_eq!(4, cell.into_inner())
444    }
445    
446    #[test]
447    pub fn test_sync_cell_mutable_borrow() {
448        let cell = SyncCell::new(4);
449
450        let mut borrow = cell.borrow_mut();
451
452        *borrow = 5;
453
454        drop(borrow);
455
456        assert_eq!(5, cell.get())
457    }
458    
459    #[test]
460    #[should_panic]
461    pub fn test_sync_cell_mutable_borrow_poisoned() {
462        let cell = Arc::new(SyncCell::new(4));
463
464        let cell2 = cell.clone();
465
466        let _ = thread::spawn(move || {
467            let _borrow = cell2.borrow();
468
469            panic!("Intentional panic.");
470        }).join();
471
472        let mut borrow = cell.borrow_mut();
473
474        *borrow = 5;
475
476        drop(borrow);
477
478        assert_ne!(5, cell.get())
479    }
480    
481    #[test]
482    #[should_panic]
483    pub fn test_sync_cell_get_poisoned() {
484        let cell = Arc::new(SyncCell::new(4));
485
486        let cell2 = cell.clone();
487
488        let _ = thread::spawn(move || {
489            let _borrow = cell2.borrow();
490
491            panic!("Intentional panic.");
492        }).join();
493
494        assert_ne!(4, cell.get())
495    }
496    
497    #[test]
498    #[should_panic]
499    pub fn test_sync_cell_set_poisoned() {
500        let cell = Arc::new(SyncCell::new(4));
501
502        let cell2 = cell.clone();
503
504        let _ = thread::spawn(move || {
505            let _borrow = cell2.borrow();
506
507            panic!("Intentional panic.");
508        }).join();
509
510        cell.set(5);
511
512        assert_ne!(5, cell.get());
513    }
514
515    #[test]
516    pub fn test_held_sync_cell_new() {
517        let _cell = HeldSyncCell::new(0);
518    }
519    
520    #[test]
521    pub fn test_held_sync_cell_get() {
522        let cell = HeldSyncCell::new(1);
523
524        assert_eq!(1, cell.get())
525    }
526    
527    #[test]
528    pub fn test_held_sync_cell_set_no_update() {
529        let cell = HeldSyncCell::new(1);
530
531        cell.set(2);
532
533        assert_eq!(true, cell.has_update());
534        assert_eq!(1, cell.get())
535    }
536    
537    #[test]
538    pub fn test_held_sync_cell_set_update() {
539        let cell = HeldSyncCell::new(1);
540
541        cell.set(2);
542        cell.update();
543
544        assert_eq!(false, cell.has_update());
545        assert_eq!(2, cell.get())
546    }
547    
548    #[test]
549    pub fn test_held_sync_cell_set_double_update() {
550        let cell = HeldSyncCell::new(1);
551
552        cell.set(2);
553        cell.update();
554        cell.update();
555
556        assert_eq!(false, cell.has_update());
557        assert_eq!(2, cell.get())
558    }
559
560    #[test]
561    pub fn test_held_sync_cell_no_set_update() {
562        let cell = HeldSyncCell::new(1);
563
564        cell.update();
565
566        assert_eq!(false, cell.has_update());
567        assert_eq!(1, cell.get())
568    }
569
570    #[test]
571    pub fn test_held_sync_cell_no_set() {
572        let cell = HeldSyncCell::new(1);
573
574        assert_eq!(false, cell.has_update());
575        assert_eq!(1, cell.get())
576    }
577    
578    #[test]
579    pub fn test_held_sync_cell_into_inner() {
580        let cell = HeldSyncCell::new(4);
581
582        assert_eq!(4, cell.into_inner())
583    }
584    
585    #[test]
586    pub fn test_held_sync_cell_set_into_inner() {
587        let cell = HeldSyncCell::new(4);
588
589        cell.set(5);
590
591        assert_eq!(5, cell.into_inner())
592    }
593    
594    #[test]
595    pub fn test_held_sync_cell_set_update_into_inner() {
596        let cell = HeldSyncCell::new(4);
597
598        cell.set(5);
599        cell.update();
600
601        assert_eq!(5, cell.into_inner())
602    }
603    
604    #[test]
605    pub fn test_held_sync_cell_mutable_borrow() {
606        let cell = HeldSyncCell::new(4);
607
608        let mut borrow = cell.borrow_mut();
609
610        *borrow = 5;
611
612        drop(borrow);
613
614        assert_eq!(5, cell.get())
615    }
616    
617    #[test]
618    pub fn test_held_sync_cell_mutable_borrow_set() {
619        let cell = HeldSyncCell::new(4);
620
621        let mut borrow = cell.borrow_mut();
622
623        *borrow = 5;
624
625        cell.set(6);
626
627        drop(borrow);
628
629        assert_eq!(5, cell.get());
630        cell.update();
631        assert_eq!(6, cell.get());
632    }
633}
634