lockable/
guard.rs

1use derive_more::{Display, Error};
2use std::borrow::Borrow;
3use std::fmt::{self, Debug};
4use std::hash::Hash;
5use std::marker::PhantomData;
6
7use crate::lockable_map_impl::{EntryValue, LockableMapConfig};
8use crate::utils::primary_arc::ReplicaOwnedMutexGuard;
9
10use super::lockable_map_impl::LockableMapImpl;
11
12/// A RAII implementation of a scoped lock for locks from a [LockableHashMap](super::LockableHashMap) or [LockableLruCache](super::LockableLruCache). When this instance is dropped (falls out of scope), the lock will be unlocked.
13#[must_use = "if unused the Mutex will immediately unlock"]
14pub struct Guard<K, V, C, P>
15where
16    K: Eq + PartialEq + Hash + Clone,
17    C: LockableMapConfig + Clone,
18    P: Borrow<LockableMapImpl<K, V, C>>,
19{
20    map: P,
21    key: K,
22    // Invariant: Is always Some(OwnedMutexGuard) unless in the middle of destruction
23    guard: Option<ReplicaOwnedMutexGuard<EntryValue<C::WrappedV<V>>>>,
24    _c: PhantomData<C>,
25    _v: PhantomData<V>,
26}
27
28impl<K, V, C, P> Guard<K, V, C, P>
29where
30    K: Eq + PartialEq + Hash + Clone,
31    C: LockableMapConfig + Clone,
32    P: Borrow<LockableMapImpl<K, V, C>>,
33{
34    pub(super) fn new(
35        map: P,
36        key: K,
37        guard: ReplicaOwnedMutexGuard<EntryValue<C::WrappedV<V>>>,
38    ) -> Self {
39        Self {
40            map,
41            key,
42            guard: Some(guard),
43            _c: PhantomData,
44            _v: PhantomData,
45        }
46    }
47
48    #[inline]
49    fn _guard(&self) -> &ReplicaOwnedMutexGuard<EntryValue<C::WrappedV<V>>> {
50        self.guard
51            .as_ref()
52            .expect("The self.guard field must always be set unless this was already destructed")
53    }
54
55    #[inline]
56    fn _guard_mut(&mut self) -> &mut ReplicaOwnedMutexGuard<EntryValue<C::WrappedV<V>>> {
57        self.guard
58            .as_mut()
59            .expect("The self.guard field must always be set unless this was already destructed")
60    }
61
62    /// Returns the key of the entry that was locked with this guard.
63    ///
64    /// Examples
65    /// -----
66    /// ```
67    /// use lockable::{AsyncLimit, LockableHashMap};
68    ///
69    /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
70    /// let lockable_map = LockableHashMap::<i64, String>::new();
71    /// let guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
72    ///
73    /// assert_eq!(4, *guard.key());
74    /// # Ok::<(), lockable::Never>(())}).unwrap();
75    /// ```
76    #[inline]
77    pub fn key(&self) -> &K {
78        &self.key
79    }
80
81    #[inline]
82    pub(super) fn value_raw(&self) -> Option<&C::WrappedV<V>> {
83        self._guard().value.as_ref()
84    }
85
86    /// Returns the value of the entry that was locked with this guard.
87    ///
88    /// If the locked entry didn't exist, then this returns None, but the guard still represents a lock on this key
89    /// and no other thread or task can lock the same key.
90    ///
91    /// Examples
92    /// -----
93    /// ```
94    /// use lockable::{AsyncLimit, LockableHashMap};
95    ///
96    /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
97    /// let lockable_map = LockableHashMap::<i64, String>::new();
98    /// {
99    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
100    ///
101    ///     // Entry doesn't exist yet
102    ///     assert_eq!(None, guard.value());
103    ///
104    ///     // Insert the entry
105    ///     guard.insert(String::from("Hello World"));
106    /// }
107    /// {
108    ///     let guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
109    ///
110    ///     // Now this entry exists
111    ///     assert_eq!(Some(&String::from("Hello World")), guard.value());
112    /// }
113    /// # Ok::<(), lockable::Never>(())}).unwrap();
114    /// ```
115    #[inline]
116    pub fn value(&self) -> Option<&V> {
117        // We're returning Option<&V> instead of &Option<V> so that
118        // user code can't change the Option from None to Some or the other
119        // way round. They should use Self::insert() and Self::remove() for that.
120        self.value_raw().map(C::borrow_value)
121    }
122
123    /// Returns the value of the entry that was locked with this guard.
124    ///
125    /// If the locked entry didn't exist, then this returns None, but the guard still represents a lock on this key
126    /// and no other thread or task can lock the same key.
127    ///
128    /// Examples
129    /// -----
130    /// ```
131    /// use lockable::{AsyncLimit, LockableHashMap};
132    ///
133    /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
134    /// let lockable_map = LockableHashMap::<i64, String>::new();
135    /// {
136    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
137    ///
138    ///     // Entry doesn't exist yet
139    ///     assert_eq!(None, guard.value_mut());
140    ///
141    ///     // Insert the entry
142    ///     guard.insert(String::from("Hello World"));
143    /// }
144    /// {
145    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
146    ///
147    ///     // Modify the value
148    ///     *guard.value_mut().unwrap() = String::from("New Value");
149    /// }
150    /// {
151    ///     let guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
152    ///
153    ///     // Now it has the new value
154    ///     assert_eq!(Some(&String::from("New Value")), guard.value());
155    /// }
156    /// # Ok::<(), lockable::Never>(())}).unwrap();
157    /// ```
158    #[inline]
159    pub fn value_mut(&mut self) -> Option<&mut V> {
160        // We're returning Option<&M::V> instead of &Option<M::V> so that
161        // user code can't change the Option from None to Some or the other
162        // way round. They should use Self::insert() and Self::remove() for that.
163        self._guard_mut().value.as_mut().map(C::borrow_value_mut)
164    }
165
166    /// Removes the entry this guard has locked from the map.
167    ///
168    /// If the entry existed, its value is returned. If the entry didn't exist, [None] is returned.
169    ///
170    /// Examples
171    /// -----
172    /// ```
173    /// use lockable::{AsyncLimit, LockableHashMap};
174    ///
175    /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
176    /// let lockable_map = LockableHashMap::<i64, String>::new();
177    /// {
178    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
179    ///
180    ///     // Insert the entry
181    ///     guard.insert(String::from("Hello World"));
182    /// }
183    /// {
184    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
185    ///
186    ///     // The value exists
187    ///     assert_eq!(Some(&String::from("Hello World")), guard.value());
188    ///
189    ///     // Remove the value
190    ///     guard.remove();
191    /// }
192    /// {
193    ///     let guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
194    ///
195    ///     // Now the value doesn't exist anymore
196    ///     assert_eq!(None, guard.value());
197    /// }
198    /// # Ok::<(), lockable::Never>(())}).unwrap();
199    /// ```
200    #[inline]
201    pub fn remove(&mut self) -> Option<V> {
202        // Setting this to None will cause Lockable::_unlock() to remove it
203        let removed_value = self._guard_mut().value.take();
204        removed_value.map(C::unwrap_value)
205    }
206
207    /// Inserts a value for the entry this guard has locked to the map.
208    ///
209    /// If the entry existed already, its old value is returned. If the entry didn't exist yet, [None] is returned.
210    /// In both cases, the map will contain the new value after the call.
211    ///
212    /// Examples
213    /// -----
214    /// ```
215    /// use lockable::{AsyncLimit, LockableHashMap};
216    ///
217    /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
218    /// let lockable_map = LockableHashMap::<i64, String>::new();
219    /// {
220    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
221    ///
222    ///     // Insert the entry
223    ///     let prev_entry = guard.insert(String::from("Hello World"));
224    ///
225    ///     // The value didn't exist previously
226    ///     assert_eq!(None, prev_entry);
227    /// }
228    /// {
229    ///     let guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
230    ///
231    ///     // Now the value exists
232    ///     assert_eq!(Some(&String::from("Hello World")), guard.value());
233    /// }
234    /// # Ok::<(), lockable::Never>(())}).unwrap();
235    /// ```
236    #[inline]
237    pub fn insert(&mut self, value: V) -> Option<V> {
238        let new_value = self.map.borrow().config().wrap_value(value);
239        let old_value = self._guard_mut().value.replace(new_value);
240        old_value.map(C::unwrap_value)
241    }
242
243    /// Inserts a value for the entry this guard has locked to the map if it didn't exist yet.
244    /// If it already existed, this call returns [TryInsertError::AlreadyExists] instead.
245    ///
246    /// This function also returns a mutable reference to the new entry, which can be used to further modify it.
247    ///
248    /// Examples
249    /// -----
250    /// ```
251    /// use lockable::{AsyncLimit, LockableHashMap};
252    ///
253    /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
254    /// let lockable_map = LockableHashMap::<i64, String>::new();
255    /// {
256    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
257    ///
258    ///     // Insert the entry
259    ///     let insert_result = guard.try_insert(String::from("Hello World"));
260    ///     assert!(insert_result.is_ok());
261    /// }
262    /// {
263    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
264    ///
265    ///     // We cannot insert it again because it already exists
266    ///     let insert_result = guard.try_insert(String::from("Hello World"));
267    ///     assert!(insert_result.is_err());
268    /// }
269    /// # Ok::<(), lockable::Never>(())}).unwrap();
270    /// ```
271    #[inline]
272    pub fn try_insert(&mut self, value: V) -> Result<&mut V, TryInsertError<V>> {
273        let config = self.map.borrow().config().clone();
274        let guard = self._guard_mut();
275        if guard.value.is_none() {
276            let new_value = config.wrap_value(value);
277            guard.value = Some(new_value);
278            Ok(C::borrow_value_mut(
279                &mut *guard.value.as_mut().expect("We just created this item"),
280            ))
281        } else {
282            Err(TryInsertError::AlreadyExists { value })
283        }
284    }
285
286    /// Returns a mutable reference to the value of the entry this guard has locked.
287    ///
288    /// If the entry doesn't exist, then `value_fn` is invoked to create it, the value
289    /// is added to the map, and then a mutable reference to it is returned.
290    ///
291    /// Examples
292    /// -----
293    /// ```
294    /// use lockable::{AsyncLimit, LockableHashMap};
295    ///
296    /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
297    /// let lockable_map = LockableHashMap::<i64, String>::new();
298    /// {
299    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
300    ///
301    ///     // Entry doesn't exist yet, `value_or_insert_with` will create it
302    ///     let value = guard.value_or_insert_with(|| String::from("Old Value"));
303    ///     assert_eq!(&String::from("Old Value"), value);
304    /// }
305    /// {
306    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
307    ///
308    ///     // Since the entry already exists, `value_or_insert_with` will not create it
309    ///     // but return the existing value instead.
310    ///     let value = guard.value_or_insert_with(|| String::from("New Value"));
311    ///     assert_eq!(&String::from("Old Value"), value);
312    /// }
313    /// # Ok::<(), lockable::Never>(())}).unwrap();
314    /// ```
315    #[inline]
316    pub fn value_or_insert_with(&mut self, value_fn: impl FnOnce() -> V) -> &mut V {
317        let config = self.map.borrow().config().clone();
318        let guard = self._guard_mut();
319        if guard.value.is_none() {
320            let new_value = config.wrap_value(value_fn());
321            guard.value = Some(new_value);
322        }
323        C::borrow_value_mut(
324            &mut *guard
325                .value
326                .as_mut()
327                .expect("We just created this item if it didn't already exist"),
328        )
329    }
330
331    /// Returns a mutable reference to the value of the entry this guard has locked.
332    ///
333    /// If the entry doesn't exist, then `value` is inserted into the map for this entry,
334    /// and then a mutable reference to it is returned.
335    ///
336    /// Examples
337    /// -----
338    /// ```
339    /// use lockable::{AsyncLimit, LockableHashMap};
340    ///
341    /// # tokio::runtime::Runtime::new().unwrap().block_on(async {
342    /// let lockable_map = LockableHashMap::<i64, String>::new();
343    /// {
344    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
345    ///
346    ///     // Entry doesn't exist yet, `value_or_insert_with` will create it
347    ///     let value = guard.value_or_insert(String::from("Old Value"));
348    ///     assert_eq!(&String::from("Old Value"), value);
349    /// }
350    /// {
351    ///     let mut guard = lockable_map.async_lock(4, AsyncLimit::no_limit()).await?;
352    ///
353    ///     // Since the entry already exists, `value_or_insert_with` will not create it
354    ///     // but return the existing value instead.
355    ///     let value = guard.value_or_insert(String::from("New Value"));
356    ///     assert_eq!(&String::from("Old Value"), value);
357    /// }
358    /// # Ok::<(), lockable::Never>(())}).unwrap();
359    /// ```
360    #[inline]
361    pub fn value_or_insert(&mut self, value: V) -> &mut V {
362        self.value_or_insert_with(move || value)
363    }
364}
365
366impl<K, V, C, P> Drop for Guard<K, V, C, P>
367where
368    K: Eq + PartialEq + Hash + Clone,
369    C: LockableMapConfig + Clone,
370    P: Borrow<LockableMapImpl<K, V, C>>,
371{
372    fn drop(&mut self) {
373        let guard = self
374            .guard
375            .take()
376            .expect("The self.guard field must always be set unless this was already destructed");
377        self.map.borrow()._unlock(&self.key, guard);
378    }
379}
380
381impl<K, V, C, P> Debug for Guard<K, V, C, P>
382where
383    K: Eq + PartialEq + Hash + Clone + Debug,
384    C: LockableMapConfig + Clone,
385    P: Borrow<LockableMapImpl<K, V, C>>,
386{
387    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
388        write!(f, "Guard({:?})", self.key)
389    }
390}
391
392/// This error is thrown by [Guard::try_insert] if the entry already exists
393#[derive(Error, Debug, Display, PartialEq, Eq, Hash, Clone, Copy)]
394pub enum TryInsertError<V> {
395    /// The entry couldn't be inserted because it already exists
396    #[display("The entry couldn't be inserted because it already exists")]
397    AlreadyExists {
398        /// The value that was attempted to be inserted
399        value: V,
400    },
401}
402
403#[cfg(test)]
404mod tests {
405    use crate::{LockableHashMap, SyncLimit};
406
407    #[test]
408    fn test_debug() {
409        let map = LockableHashMap::<i64, String>::new();
410        let guard = map.blocking_lock(4, SyncLimit::no_limit()).unwrap();
411        assert_eq!("Guard(4)", format!("{:?}", guard));
412    }
413}