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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
//! A library for high concurrency reads.
//!
//! This library is named after the 2 (identical) tables that are held internally:
//! - Active - this is the table that all Readers view. This table will never be
//!   write locked, so readers never face contention.
//! - Standby - this is the table that writers mutate. A writer should face
//!   minimal contention retrieving this table since Readers move to the Active
//!   table whenever calling `.read()`.
//!
//! There are 2 ways to use this crate:
//! 1. Direct interaction with `AsLock`/`AsLockHandle`. This is more flexible 
//!    since users can pass in any struct they want and mutate it however they
//!    choose. All updates though, will need to be done by passing a function
//!    instead of via mutable methods (`UpdateTables` trait).
//! 2. Using collections which are built out of the primitives but which provide an
//!    API similar to `RwLock<T>`; writers can directly call to methods without
//!    having to provide a mutator function.
//!
//! There are 2 flavors/modules:
//! 1. Lockless - this variant trades off increased performance against changing the
//!    API to be less like a `RwLock`. This centers around the `AsLockHandle`, which
//!    is conceptually similar to `Arc<RwLock>` (requires a separate `AsLockHandle`
//!    per thread/task).
//! 2. Sync - this centers around using an `AsLock`, which is meant to feel like a
//!    `RwLock`. The main difference is that you still cannot gain direct write
//!    access to the underlying table due to the need to keep them identical.
//!
//! The cost of minimizing contention is:
//! 1. Memory - Internally there are 2 copies of the underlying type the user
//!    created. This is needed to allow there to always be a table that Readers can
//!    access out without contention.
//! 2. CPU - The writer must apply all updates twice, once to each table. Lock
//!    contention for the writer should be less than with a plain RwLock due to
//!    Readers using the active_table, so it's possible that write times themselves
//!    will drop.
//!
//! ### Example
//! Example of the 3 usage patterns: build your own wrapper, use prebuilt
//! collections, and use the primitives. Each of these can be done with both
//! sync and lockless.
//! ```rust
//! use std::thread::sleep;
//! use std::time::Duration;
//! use std::sync::Arc;
//!
//! // Create wrapper class so that users can interact with the active_standby
//! // struct via a RwLock-like interface. See the implementation of the
//! // collections for more examples.
//! mod wrapper {
//!     use active_standby::UpdateTables;
//!
//!     active_standby::generate_lockless_aslockhandle!(i32);
//!
//!     struct AddOne {}
//!
//!     impl<'a> UpdateTables<'a, i32, ()> for AddOne {
//!         fn apply_first(&mut self, table: &'a mut i32) {
//!             *table = *table + 1;
//!         }
//!         fn apply_second(mut self, table: &mut i32) {
//!             self.apply_first(table);
//!         }
//!     }
//!
//!     // Client's must implement the mutable interface that they want to
//!     // offer users. Non mutable functions are automatic via Deref.
//!     impl<'w> AsLockWriteGuard<'w> {
//!         pub fn add_one(&mut self) {
//!             self.guard.update_tables(AddOne {})
//!         }
//!     }
//! }
//!
//! pub fn run_wrapper() {
//!     let table = wrapper::AsLockHandle::new(0);
//!     let table2 = table.clone();
//!
//!     let handle = std::thread::spawn(move || {
//!         while *table2.read() != 1 {
//!             sleep(Duration::from_micros(100));
//!         }
//!     });
//!
//!     table.write().add_one();
//!     handle.join();
//! }
//!
//! // Use a premade collection which wraps `AsLock<Vec<T>>`, to provide an
//! // interface akin to `RwLock<Vec<T>>`.
//! pub fn run_collection() {
//!     use active_standby::sync::collections::AsVec;
//! 
//!     let table = Arc::new(AsVec::default());
//!     let table2 = Arc::clone(&table);
//!
//!     let handle = std::thread::spawn(move || {
//!         while *table2.read() != vec![1] {
//!             sleep(Duration::from_micros(100));
//!         }
//!     });
//!
//!     table.write().push(1);
//!     handle.join();
//! }
//!
//! // Use the raw AsLock interface to update the underlying data.
//! pub fn run_primitive() {
//!     use active_standby::sync::AsLock;
//!
//!     // If the entries in your table are large, you may want to hold only
//!     // 1 copy shared by both tables. This is safe so long as you never
//!     // mutate the shared data; only remove and replace it in the table.
//!     let table = Arc::new(AsLock::new(vec![Arc::new(1)]));
//!     let table2 = Arc::clone(&table);
//!
//!     let handle = std::thread::spawn(move || {
//!         while *table2.read() != vec![Arc::new(2)] {
//!             sleep(Duration::from_micros(100));
//!         }
//!     });
//!
//!     table.write().update_tables_closure(|table| {
//!         // Update the entry in the table, not the shared value behind the
//!         // Arc.
//!         table[0] = Arc::new(2);
//!     });
//!     handle.join();
//! }
//!
//! fn main() {
//!     run_wrapper();
//!     run_collection();
//!     run_primitive();
//! }
//! ```
//!
//! ## Testing
//! There are a number of tests that come with active_standby (see
//! tests/tests_script.sh for examples):
//!
//! [unittests](https://doc.rust-lang.org/book/ch11-01-writing-tests.html)
//!
//! [benchmarks](https://doc.rust-lang.org/unstable-book/library-features/test.html)
//!
//! [loom](https://crates.io/crates/loom)
//!
//! [LLVM Sanitizers](https://doc.rust-lang.org/beta/unstable-book/compiler-flags/sanitizer.html)
//!
//! [Miri](https://github.com/rust-lang/miri)
//!
//! [Rudra](https://github.com/sslab-gatech/Rudra)

mod macros;
pub(crate) mod types;

mod collections;
mod primitives;

pub use crate::types::UpdateTables;
pub mod lockless {

    /// Premade structs which wrap standard collection in the active standby
    /// model. This allows for the API to match RwLock<T> while under the hood
    /// the active standby model works its magic. AsLockReadGuard is generic
    /// since only writes require a special API for active_standby.
    pub mod collections {
        // Inline the re-export to make rustdocs more readable.
        #[doc(inline)]
        pub use crate::collections::btreemap::lockless::{
            AsLockHandle as AsBTreeMapHandle, AsLockWriteGuard as AsBTreeMapWriteGuard,
        };
        #[doc(inline)]
        pub use crate::collections::btreeset::lockless::{
            AsLockHandle as AsBTreeSetHandle, AsLockWriteGuard as AsBTreeSetWriteGuard,
        };
        #[doc(inline)]
        pub use crate::collections::hashmap::lockless::{
            AsLockHandle as AsHashMapHandle, AsLockWriteGuard as AsHashMapWriteGuard,
        };
        #[doc(inline)]
        pub use crate::collections::hashset::lockless::{
            AsLockHandle as AsHashSetHandle, AsLockWriteGuard as AsHashSetWriteGuard,
        };
        #[doc(inline)]
        pub use crate::collections::vec::lockless::{
            AsLockHandle as AsVecHandle, AsLockWriteGuard as AsVecWriteGuard,
        };
    }
    pub use crate::primitives::lockless::{AsLockHandle, AsLockReadGuard, AsLockWriteGuard};
}

pub mod sync {
    /// Premade structs which wrap standard collection in the active standby
    /// model. This allows for the API to match RwLock<T> while under the hood
    /// the active standby model works its magic. AsLockReadGuard is generic
    /// since only writes require a special API for active_standby.
    pub mod collections {
        // Inline the re-export to make rustdocs more readable.
        #[doc(inline)]
        pub use crate::collections::btreemap::sync::{
            AsLock as AsBTreeMap, AsLockWriteGuard as AsBTreeMapWriteGuard,
        };
        #[doc(inline)]
        pub use crate::collections::btreeset::sync::{
            AsLock as AsBTreeSet, AsLockWriteGuard as AsBTreeSetWriteGuard,
        };
        #[doc(inline)]
        pub use crate::collections::hashmap::sync::{
            AsLock as AsHashMap, AsLockWriteGuard as AsHashMapWriteGuard,
        };
        #[doc(inline)]
        pub use crate::collections::hashset::sync::{
            AsLock as AsHashSet, AsLockWriteGuard as AsHashSetWriteGuard,
        };
        #[doc(inline)]
        pub use crate::collections::vec::sync::{
            AsLock as AsVec, AsLockWriteGuard as AsVecWriteGuard,
        };
    }
    pub use crate::primitives::sync::{AsLock, AsLockReadGuard, AsLockWriteGuard};
}