5 stable releases
4.0.8 | Apr 25, 2019 |
---|---|
4.0.7 | Jan 12, 2019 |
4.0.2 |
|
3.0.11 |
|
1.0.4 |
|
#900 in Concurrency
65 downloads per month
85KB
1.5K
SLoC
Voluntary Servitude
Features
-
Atomic abstractions (
Atomic
,AtomicOption
,FillOnceAtomicOption
,FillOnceAtomicArc
) -
Thread-safe appendable list with a lock-free iterator (
VoluntaryServitude
- also calledVS
) -
Serde serialization (
serde-traits
feature) -
par_extend
,from_par_iter
rayon implementation (rayon-traits
feature) -
Logging (
logs
feature)You probably only need this if you are debugging this crate
Atomic abstractions
- Atomic -> atomic
Box<T>
- AtomicOption -> atomic
Option<Box<T>>
- FillOnceAtomicOption -> atomic
Option<Box<T>>
that can give references (ideal for iterators) - FillOnceAtomicArc -> atomic
Option<Arc<T>>
with a limited API (likeFillOnceAtomicOption
)
With Atomic
and AtomicOption
it's not safe to get a reference, you must replace the value to access it.
To safely get a reference of T you must use FillOnceAtomicOption
and accept the API limitations (initially None
but can be filled once).
For a safe AtomicArc
you must use some data-structure from arc-swap
, RwLock/Mutex
from parking_lot
(or std
, which is slower but the standard) or FillOnceAtomicArc
and accept the limited API (2018).
Licenses
MIT and Apache-2.0
VoluntaryServitude
Examples
Single thread
use voluntary_servitude::vs;
fn main() {
let (a, b, c) = (0usize, 1usize, 2usize);
// VS alias to VoluntaryServitude
// vs! alias to voluntary_servitude! (and operates like vec!)
let list = vs![a, b, c];
assert_eq!(list.iter().collect::<Vec<_>>(), vec![&a, &b, &c]);
// Current VS's length
// Be careful with race conditions since the value, when used, may not be true anymore
assert_eq!(list.len(), 3);
// The 'iter' method makes a lock-free iterator (Iter)
for (index, element) in list.iter().enumerate() {
assert_eq!(index, *element);
}
// You can get the current iteration index
// iter.index() == iter.len() means iteration ended (iter.next() == None)
let mut iter = &mut list.iter();
assert_eq!(iter.index(), 0);
assert_eq!(iter.next(), Some(&0));
assert_eq!(iter.index(), 1);
// List can also be cleared (but current iterators are not affected)
list.clear();
assert_eq!(iter.len(), 3);
assert_eq!(list.len(), 0);
assert_eq!(list.iter().len(), 0);
assert_eq!((&mut list.iter()).next(), None);
println!("Single thread example ended without errors");
}
Multi-producer, multi-consumer
use voluntary_servitude::vs;
use std::{sync::Arc, thread::spawn};
const CONSUMERS: usize = 8;
const PRODUCERS: usize = 4;
const ELEMENTS: usize = 10_000_000;
fn main() {
let list = Arc::new(vs![]);
let mut handlers = vec![];
// Creates producer threads to insert 10k elements
for _ in 0..PRODUCERS {
let l = Arc::clone(&list);
handlers.push(spawn(move || {
let _ = (0..ELEMENTS).map(|i| l.append(i)).count();
}));
}
// Creates consumer threads to print number of elements
// Until all of them are inserted
for _ in 0..CONSUMERS {
const TOTAL: usize = PRODUCERS * ELEMENTS;
let consumer = Arc::clone(&list);
handlers.push(spawn(move || loop {
let count = consumer.iter().count();
println!("{} elements", count);
if count >= TOTAL { break };
}));
}
// Join threads
for handler in handlers.into_iter() {
handler.join().expect("Failed to join thread");
}
println!("Multi-thread example ended without errors");
}
Dependencies
~1.2–1.9MB
~32K SLoC