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
#![deny(missing_docs)] //! A lock-free (when reading), eventually consistent synchronization primitive. //! //! This primitive makes reading and writing possible at the same time, although refreshing is //! needed to make writes visible to the readers. //! //! This crate is very similar to [`evmap`](https://docs.rs/evmap), but generalized to any type. //! Unlike `evmap`, which wraps a HashMap, `evc` is lower level, meaning that you need to be //! able to cache all possible mutations on the inner type (`OperationCache`). Therefore making //! an extension trait and implementing it for `WriteHandle<YourType>` is encouraged, so that //! accessing the inner data can be done using regular methods (like `evmap` does internally). //! //! # Examples //! //! `VecWrapper` //! //! ``` //! use evc::OperationCache; //! //! #[derive(Clone, Debug, Default)] //! struct VecWrapper(Vec<u16>); //! //! #[derive(Clone, Copy, Debug)] //! enum Operation { //! Push(u16), //! Remove(usize), //! Clear, //! } //! //! impl OperationCache for VecWrapper { //! type Operation = Operation; //! //! fn apply_operation(&mut self, operation: Self::Operation) { //! match operation { //! Operation::Push(value) => self.0.push(value), //! Operation::Remove(index) => { self.0.remove(index); }, //! Operation::Clear => self.0.clear(), //! } //! } //! } //! //! let (mut w_handle, r_handle) = evc::new(VecWrapper::default()); //! //! w_handle.write(Operation::Push(42)); //! w_handle.write(Operation::Push(24)); //! //! assert_eq!(r_handle.read().0, &[]); //! //! w_handle.refresh(); //! //! assert_eq!(r_handle.read().0, &[42, 24]); //! //! w_handle.write(Operation::Push(55)); //! w_handle.write(Operation::Remove(0)); //! w_handle.refresh(); //! //! assert_eq!(r_handle.read().0, &[24, 55]); //! //! w_handle.write(Operation::Clear); //! //! assert_eq!(r_handle.read().0, &[24, 55]); //! //! w_handle.refresh(); //! //! assert_eq!(r_handle.read().0, &[]); //! //! ``` use std::mem; use std::sync::{Arc, Mutex, Weak}; use std::sync::atomic::{AtomicPtr, AtomicUsize}; mod read; pub use read::{ReadHandle, ReadHandleFactory, ReadHandleGuard}; mod write; pub use write::WriteHandle; pub(crate) type Epoch = Arc<AtomicUsize>; pub(crate) type WeakEpoch = Weak<AtomicUsize>; pub(crate) type Epochs = Arc<Mutex<Vec<WeakEpoch>>>; /// Represents anything that can be mutated using operations. This trait has to be implemented in /// order to store it in an `evc`. pub trait OperationCache { /// The operation this type uses for modifying itself. type Operation: Clone; /// Apply an operation to self. fn apply_operation(&mut self, operations: Self::Operation); } pub(crate) struct Inner<T> { value: T, } pub(crate) const USIZE_MSB: usize = 1 << (mem::size_of::<usize>() * 8 - 1); /// Create a write handle and a read handle to some data. The data must be both `OperationCache`, /// to support queuing data (so that both buffers can be modified during refreshes), and `Clone`, /// to make double buffering possible. pub fn new<T: Clone + OperationCache>(value: T) -> (WriteHandle<T>, ReadHandle<T>) { let readers_inner = Arc::new(AtomicPtr::new(Box::into_raw(Box::new(Inner { value: value.clone() })))); let writers_inner = Arc::new(AtomicPtr::new(Box::into_raw(Box::new(Inner { value })))); let epochs = Arc::new(Mutex::new(Vec::new())); let read_handle = ReadHandle::new(Arc::clone(&readers_inner), Arc::clone(&epochs)); let write_handle = WriteHandle::new(writers_inner, readers_inner, epochs); (write_handle, read_handle) }