Struct lockfree::incin::Incinerator
source · pub struct Incinerator<T> { /* private fields */ }
Expand description
The incinerator. It is an API used to solve the infamous ABA problem. It basically consists of a counter and a list of garbage. Before a thread begins a suffering-from-ABA operation, it should start a new pause, and keep the incinerator paused while it is performing the operation.
When a thread wants to drop an allocation that might affect other threads
with ABA problem or uses-after-free, it should add
it to the incinerator’s
garbage list. The incinerator will only execute the drop
of its type T
when the pause counter is zero.
When the incinerator is dropped, all the garbage is automatically dropped too.
C11 Implementation: https://gitlab.com/bzim/c11-incinerator/
Example
extern crate lockfree;
use lockfree::incin::Incinerator;
use std::{
ptr::{null_mut, NonNull},
sync::{
atomic::{AtomicPtr, Ordering::*},
Arc,
},
thread,
};
let incin = Arc::new(Incinerator::<Box<u128>>::new());
let ptr = Box::into_raw(Box::new(55u128));
let dummy_state = Arc::new(AtomicPtr::new(ptr));
let mut threads = Vec::with_capacity(16);
for i in 0 .. 16 {
let state = dummy_state.clone();
let incin = incin.clone();
threads.push(thread::spawn(move || {
let ptr = incin.pause_with(|_| {
let loaded = state.load(SeqCst);
let new = unsafe { *loaded + i };
state.swap(Box::into_raw(Box::new(new)), SeqCst)
});
// dropping
incin.add(unsafe { Box::from_raw(ptr) })
}));
}
for thread in threads {
thread.join().unwrap();
}
let boxed = unsafe { Box::from_raw(dummy_state.load(SeqCst)) };
assert!(*boxed <= 15 * 15);
Implementations
sourceimpl<T> Incinerator<T>
impl<T> Incinerator<T>
sourcepub fn pause(&self) -> Pause<'_, T>
pub fn pause(&self) -> Pause<'_, T>
Increments the pause counter and creates a pause associated with this
incinerator. Only after creating the pause you should perform atomic
operations such as load
and any other operation affected by ABA
problem. This operation performs AcqRel
on the pause counter.
sourcepub fn pause_with<F, A>(&self, exec: F) -> Awhere
F: FnOnce(&Pause<'_, T>) -> A,
pub fn pause_with<F, A>(&self, exec: F) -> Awhere
F: FnOnce(&Pause<'_, T>) -> A,
Creates a pause before executing the given closure and resumes the
incinerator only after executing the closure. You should execute the
whole ABA-problem-suffering cycle of load
and compare_and_swap
inside the closure. See documentation for Incinerator::pause
and
Pause::resume
for more details.
sourcepub fn add(&self, val: T)
pub fn add(&self, val: T)
Adds the given value to the garbage list. The value is only dropped when
the counter is zero. If the counter is zero when the method is called,
the value is immediately dropped and the garbage list is cleared. You
must remove the resource from shared context before calling this method.
This operation performs Acquire
on the pause counter.