1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
//! Utility module to safely refer to a mutable Arc.
use std::sync::{Arc, RwLock};
use educe::Educe;
use crate::{Error, Result};
/// A shareable mutable-ish optional reference to a an [`Arc`].
///
/// Because you can't actually change a shared [`Arc`], this type implements
/// mutability by replacing the Arc itself with a new value. It tries
/// to avoid needless clones by taking advantage of [`Arc::make_mut`].
///
// We give this construction its own type to simplify its users, and make
// sure we don't hold the lock against any async suspend points.
#[derive(Debug, Educe)]
#[educe(Default)]
#[cfg_attr(docsrs, doc(cfg(feature = "experimental-api")))]
#[cfg_attr(not(feature = "experimental-api"), allow(unreachable_pub))]
pub struct SharedMutArc<T> {
/// Locked reference to the current value.
///
/// (It's okay to use RwLock here, because we never suspend
/// while holding the lock.)
dir: RwLock<Option<Arc<T>>>,
}
#[cfg_attr(not(feature = "experimental-api"), allow(unreachable_pub))]
impl<T> SharedMutArc<T> {
/// Construct a new empty SharedMutArc.
pub fn new() -> Self {
SharedMutArc::default()
}
/// Replace the current value with `new_val`.
pub fn replace(&self, new_val: T) {
let mut w = self
.dir
.write()
.expect("Poisoned lock for directory reference");
*w = Some(Arc::new(new_val));
}
/// Remove the current value of this SharedMutArc.
#[allow(unused)]
pub(crate) fn clear(&self) {
let mut w = self
.dir
.write()
.expect("Poisoned lock for directory reference");
*w = None;
}
/// Return a new reference to the current value, if there is one.
pub fn get(&self) -> Option<Arc<T>> {
let r = self
.dir
.read()
.expect("Poisoned lock for directory reference");
r.as_ref().map(Arc::clone)
}
/// Replace the contents of this SharedMutArc with the results of applying
/// `func` to the inner value.
///
/// Gives an error if there is no inner value.
///
/// Other threads will not abe able to access the inner value
/// while the function is running.
///
/// # Limitation: No panic-safety
///
/// If `func` panics while it's running, this object will become invalid
/// and future attempts to use it will panic. (TODO: Fix this.)
// Note: If we decide to make this type public, we'll probably
// want to fiddle with how we handle the return type.
pub fn mutate<F, U>(&self, func: F) -> Result<U>
where
F: FnOnce(&mut T) -> Result<U>,
T: Clone,
{
let mut writeable = self
.dir
.write()
.expect("Poisoned lock for directory reference");
let dir = writeable.as_mut();
match dir {
None => Err(Error::DirectoryNotPresent), // Kinda bogus.
Some(arc) => func(Arc::make_mut(arc)),
}
}
}
#[cfg(test)]
mod test {
// @@ begin test lint list maintained by maint/add_warning @@
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_duration_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
use super::*;
#[test]
fn shared_mut_arc() {
let val: SharedMutArc<Vec<u32>> = SharedMutArc::new();
assert_eq!(val.get(), None);
val.replace(Vec::new());
assert_eq!(val.get().unwrap().as_ref()[..], Vec::<u32>::new());
val.mutate(|v| {
v.push(99);
Ok(())
})
.unwrap();
assert_eq!(val.get().unwrap().as_ref()[..], [99]);
val.clear();
assert_eq!(val.get(), None);
assert!(val
.mutate(|v| {
v.push(99);
Ok(())
})
.is_err());
}
}