Skip to main content

snaplock/
std_impl.rs

1use crate::{SnapshotLock, SnapshotLockReadGuard, SnapshotLockWriteGuard};
2use arc_swap::ArcSwap;
3use std::marker::PhantomData;
4use std::ops::Deref;
5use std::sync::atomic::{AtomicUsize, Ordering};
6use std::sync::{Arc, RwLock, RwLockWriteGuard};
7
8impl<T: Clone> SnapshotLock<T, RwLock<(T, usize)>> {
9    /// Creates a new `SnapshotLock` with the initial value.
10    ///
11    /// # Arguments
12    ///
13    /// * `inner` - The initial value to be protected by the lock.
14    ///
15    /// # Examples
16    ///
17    /// ```
18    /// use snaplock::SnapshotLock;
19    ///
20    /// let lock = SnapshotLock::new(42);
21    /// assert_eq!(*lock.read(), 42);
22    /// ```
23    pub fn new(inner: T) -> Self {
24        Self {
25            inner: RwLock::new((inner.clone(), 0)),
26            latest: ArcSwap::new(Arc::new(inner)),
27            latest_version: AtomicUsize::new(0),
28        }
29    }
30
31    /// Acquires an exclusive write lock on the protected data.
32    ///
33    /// This method blocks until no other writers hold the lock. The returned
34    /// `SnapshotLockWriteGuard` allows mutable access to the data. When the guard is
35    /// dropped, the version number is incremented and subsequent reads will
36    /// see the updated value.
37    ///
38    /// # Returns
39    ///
40    /// A `SnapshotLockWriteGuard` that provides mutable access to the data.
41    ///
42    /// # Examples
43    ///
44    /// ```
45    /// use snaplock::SnapshotLock;
46    ///
47    /// let lock = SnapshotLock::new(0);
48    /// {
49    ///     let mut guard = lock.write();
50    ///     *guard = 42;
51    /// } // Guard dropped here, version incremented
52    /// assert_eq!(*lock.read(), 42);
53    /// ```
54    pub fn write(&self) -> SnapshotLockWriteGuard<'_, T, RwLockWriteGuard<'_, (T, usize)>> {
55        let mut guard = self.inner.write().unwrap();
56        guard.1 += 1; // increment version
57        SnapshotLockWriteGuard(guard, PhantomData)
58    }
59
60    /// Acquires a shared read lock on the protected data.
61    ///
62    /// This method attempts to get the latest version of the data with minimal
63    /// blocking. It first tries a non-blocking read of the current version,
64    /// and if that fails or detects a newer version, it falls back to a
65    /// cached snapshot.
66    ///
67    /// # Returns
68    ///
69    /// A `SnapshotLockReadGuard` that provides immutable access to a snapshot of the data.
70    ///
71    /// # Examples
72    ///
73    /// ```
74    /// use snaplock::SnapshotLock;
75    ///
76    /// let lock = SnapshotLock::new("data");
77    /// let guard = lock.read();
78    /// assert_eq!(*guard, "data");
79    /// ```
80    pub fn read(&self) -> SnapshotLockReadGuard<T> {
81        if let Ok(read) = self.inner.try_read() {
82            let (data, version) = read.deref();
83            if self.latest_version.swap(*version, Ordering::Acquire) < *version {
84                self.latest.store(Arc::new(data.clone()));
85            }
86        }
87
88        SnapshotLockReadGuard(arc_swap::Guard::into_inner(self.latest.load()))
89    }
90}