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}