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
//! This crate provides a set (eventually) of smart pointer types that
//! allow read access with no guards (and minimal to no overhead) and
//! no need to call [std::borrow::Borrow]. These smart pointers each
//! allow internal mutability (obtaining mutable references) by a
//! Read-Copy-Update approach, so you get a mutable reference to a
//! private copy of the data, which you can mutate at will. When the
//! mutation is complete the pointer is atomically updated. Old
//! references to the data may still exist, and will still be a valid
//! reference to the old data.
//!
//! Basically, these smart pointers allow internal mutability through
//! a slow and painful process, while keeping read-only access both
//! fast and easy (in particular, no need to call `ptr.borrow()`
//! everywhere). Write access is guarded, but read access is not.
//!
//! The names of the types are based on the standard smart pointer
//! types.
//!
//! 1. `[BoxRcu]` is an owned pointer similar to [std::box::Box]. If
//! you like, it is actually closer to `Box<RefCell<T>>`, or even
//! `Box<Mutex<T>>`, but without the nuisance of having to call
//! borrow when reading.
//!
//! 2. `[RcRcu]` is a reference counted pointer similar to [std::rc::Rc].
//! If you like, it is actually closer to `Rc<RefCell<T>>`, but
//! without the nuisance of having to call borrow when reading.
//!
//! 3. `ArcRcu` is planned to be a thread-safe reference counted
//! pointer similar to [std::sync::Arc]. It is actually
//! closer to `Arc<RwLock<T>>`, but without the nuisance of having to
//! call `read` before reading.
//!
//! ### Cleaning
//!
//! Due to this crate's read-copy-update semantics, old copies of your
//! data are kept until we are confident that there are no longer any
//! references to them. Because we do not have any guards on the read
//! references, this must be done manually. This is the cost we pay
//! for extra convenience (and much improved read speed in the case of
//! `ArcRcu`) on the read operations. You have two options for how to
//! handle this.
//!
//! One option is to simply store those extra copies until then entire
//! smart pointer itself is freed. That is what happens if you do
//! nothing, and for small data that is only mutated once, it's a fine
//! option. However, for `[ArcRcu]` and `[RcRcu]` there will be a
//! slowdown on reading until you do call clean, since an extra level
//! of pointer redirection will be required.
//!
//! The other option is to call `clean()` when convenient. `clean`
//! takes a `&mut self`, so when it is called, the compiler will prove
//! to us that there are no other references out there via *this*
//! smart pointer. For `BoxCell` that is sufficient to prove that we
//! can free the data. In the case of the reference counted data
//! pointers, we keep track of a count of how many copies have been
//! dereferenced since the last time `clean` was called. We could
//! probably be more accurate with "epoch" tracking, but I don't know
//! that the complexity will be worthwhile.
mod boxrcu;
pub use crate::boxrcu::BoxRcu;
mod rcrcu;
pub use crate::rcrcu::RcRcu;
mod arcrcu;
pub use crate::arcrcu::ArcRcu;
macro_rules! impl_stuff {
($t:ident) => {
impl<T: PartialEq> PartialEq for $t<T> {
fn eq(&self, other: &$t<T>) -> bool {
&(**self) == &(**other)
}
}
impl<T: Eq> Eq for $t<T> {}
impl<T: PartialOrd> PartialOrd for $t<T> {
fn partial_cmp(&self, other: &$t<T>) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T: Ord> Ord for $t<T> {
fn cmp(&self, other: &$t<T>) -> std::cmp::Ordering {
(**self).cmp(&**other)
}
}
}
}
impl_stuff!(BoxRcu);
impl_stuff!(RcRcu);
impl_stuff!(ArcRcu);