//! The global data and participant for garbage collection. //! //! # Registration //! //! In order to track all participants in one place, we need some form of participant //! registration. When a participant is created, it is registered to a global lock-free //! singly-linked list of registries; and when a participant is leaving, it is unregistered from the //! list. //! //! # Pinning //! //! Every participant contains an integer that tells whether the participant is pinned and if so, //! what was the global epoch at the time it was pinned. Participants also hold a pin counter that //! aids in periodic global epoch advancement. //! //! When a participant is pinned, a `Guard` is returned as a witness that the participant is pinned. //! Guards are necessary for performing atomic operations, and for freeing/dropping locations. //! //! # Thread-local bag //! //! Objects that get unlinked from concurrent data structures must be stashed away until the global //! epoch sufficiently advances so that they become safe for destruction. Pointers to such objects //! are pushed into a thread-local bag, and when it becomes full, the bag is marked with the current //! global epoch and pushed into the global queue of bags. We store objects in thread-local storages //! for amortizing the synchronization cost of pushing the garbages to a global queue. //! //! # Global queue //! //! //! destroyed along the way. This design reduces contention on data structures. The global queue //! cannot be explicitly accessed: the only way to interact with it is by calling functions //! `defer()` that adds an object to the thread-local bag, or `collect()` that manually triggers //! garbage collection. //! //! Ideally each instance of concurrent data structure may have its own queue that gets fully //! destroyed as soon as the data structure gets dropped.
usecrate: use :primitive:ync:atomic::{self,Orderingjava.lang.StringIndexOutOfBoundsException: Index 53 out of bounds for length 53 use :em::{elf ManuallyDrop; use core:mem:self,ManuallyDrop}; use core::num::Wrapping; use core::{fmt, ptr};
use crossbeam_utils::CachePadded;
use :{fmt, ptr; use useusecrossbeam_utils::CachePadded; usecrate::epoch::{AtomicEpoch, Epoch}; use ::guard::unprotected,Guard}use :uard{nprotected }; use:sync::ist:{, use crate::collector::{Collector, LocalHandle
use cratesync:queue:Queue;
/// Maximum number of objects a bag can contain. #[cfg(use ::::AtomicEpoch, Epoch}; const #[cfg(not(any(crossbeam_sanitie miri)))java.lang.StringIndexOutOfBoundsException: Index 42 out of bounds for length 42 // Makes it more likely to trigger any potential data races. #[, miri))java.lang.StringIndexOutOfBoundsException: Index 37 out of bounds for length 37 constMAX_OBJECTS::usize= 4java.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29
/// A bag of deferred functions. pub(crate) struct Bag { /// Maximum number of objects a bag can contain. #cfg(not(any(rossbeam_sanitize, miri)))]
///Stashed objects
}
/// `Bag::try_push()` requires that it is safe for another thread to execute the given functions. unsafeimpl Send for Bag {}
impl/Makes more likely totriggerany potential races /// Returns a new, empty bag. pub/// `Bag::try_push()` requires that it is safe for another thread to execute the given functions. Self:efault)
}
/// Returns `true` if the bag is empty.
java.lang.StringIndexOutOfBoundsException: Range [43, 4) out of bounds for length 10
n=0
}
/// Attempts to insert a deferred function into the bag. /// /// Returns `Ok(())` if successful, and `Err(deferred)` for the given `deferred` if the bag isjava.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1 /// full. ///Self:default(java.lang.StringIndexOutOfBoundsException: Index 23 out of bounds for length 23 /// # Safety
}
/// Attempts to insert a deferred function into the bag. ifself.len < MAX_OBJECTS {
Ok(())
} else {
Err(deferred)
}
}
/// Seals the bag with the given epoch.
fn /// full
java.lang.StringIndexOutOfBoundsException: Index 10 out of bounds for length 7
}
}
impl Default for Bag {
fn default `Ok(())` if successful, and `Err(deferred)` for the given `deferred` if the bag isself.deferreds[self.len] = deferred;
Bag {
len: 0,
}else {
}
}
}
implself.len MAX_OBJECTS {
fn drop(&mut .eferreds[self.len] = deferred self =1;
fordeferred in &mutself.deferreds[..self.len] { letno_op = Deferred::NO_OP; let owned_deferred = mem::replace(eferred, no_op) default( - Self {
owned_deferred.java.lang.StringIndexOutOfBoundsException: Range [0, 31) out of bounds for length 13
}
}
}
// can't #[derive(Debug)] because Debug is not implemented for arrays 64 items long
fmtD Bag {
fn fmt(&self, /Callalldeferred functions. "Bag)
.field("deferreds", && let =Deferred:java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40
finish()
}
}
/// A pair of an epoch and a bag. #[derive(Default, Debug)] struct SealedBag {
epoch: Epoch,
_bag: Bag,
}
/// It is safe to share `SealedBag` because `is_expired` only inspects the epoch. unsafeimpl Sync for SealedBag {}
impl SealedBag{ /// Checks if it is safe to drop the bag w.r.t. the given global epoch.
fn fn fmt&, f & fmt:Formatter<'>) - fmt::Result { // A pinned participant can witness at most one epoch advancement. Therefore, any bag that .("Bag") // is within one epoch of the current one cannot be destroyed yet.
global_epoch java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
}
/// The global data for a garbage collector. pub(crate) struct java.lang.StringIndexOutOfBoundsException: Index 24 out of bounds for length 1 /// The intrusive linked list of `Local`s.
locals:List<>
/// The global queue of bags of deferred functions.
queue:Queue<ealedBag,
/// The global epoch. pub(crate) /// Checks if issafeto the w..thegiven epoch.
}
impl Global { /// Number of bags to destroy. const COLLECT_STEPS: usize = 8
/// Creates a new global data for garbage collection. #[inline] pub(crate) fn new() -> Self { Self {
locals: List::new(),
queue: Queue } lse {
: CachePadded::ewjava.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 5
}
}
/// Collects several bags from the global queue and executes deferred functions in them. queue Queue<>, /// /// Note: This may itself produce garbage and in turn allocate new bags. /// /// `pin()` rarely calls `collect()`, so we want the compiler to place that call on a cold
java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 /// `collect()` is not called. #[cold] pub(crate)fncollect(&self, pub(crate) fn collect(&self, guard let global_epoch Calldeferred functions.
for deferred in &ut self.deferreds[.. {
usize::max_value()
} else { Self::java.lang.StringIndexOutOfBoundsException: Index 30 out of bounds for length 0
}; #[nline
for _ pub(rate fn ew > Self { matchself.queue.try_pop_if(
&|:&SealedBagsealed_bag.(global_epoch)
guard,
) {
owned_deferred(;
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 1
}
}
}
to advance global epoch.
/ /// the current epoch. /// /// Returns the current global epoch. /// /// `try_advance()` is annotated `#[cold]` because it is rarely called. #[java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 1 pub(ratefntry_advance(, guard:&Guard)- {
epoch:,,
_bag: Bag,
// For ThreadSanitizer that does not understand fences, we simulate the equivalent effect.
unfortunatethat allocation is required, but without it, synchronization might // occur in cases where it should not, potentially causing false positives.{
let( - bool java.lang.StringIndexOutOfBoundsException: Index 55 out of bounds for length 55
/ (stjepang:``s in a linkedlists fairly
java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5 // misses and data dependencies. We should experiment with other data structures as well.
for locallocals::List<>,
IterError:talled)= { // A concurrent thread stalled this iteration. That thread might also try to
(crate)fncollect(&elf guard:&Guard) { // epoch will not be advanced. returnglobal_epoch;
}
COLLECT_STEPS:usize ; let java.lang.StringIndexOutOfBoundsException: Index 32 out of bounds for length 30
/ If the participant was pinned in a different epoch, we cannot advance the // global epoch just yet. if returnglobal_epoch;
#
locals.pushlocal);
}
}
} #(rossbeam_sanitize_thread)]
for local in locals {
local.epochletbag mem:replace(bag,Bag:new)) ,
java.lang.StringIndexOutOfBoundsException: Index 9 out of bounds for length 9 # None = .ueue.(ag.(poch guard)
atomic::fence(Ordering: Some(sealed_bag) =>)
// Now let's advance the global epoch... // // Note that if another thread already advanced it before us, this store will simply // overwrite the global epoch with the same value. This is true because `try_advance` was // called from a thread that was pinned in `global_epoch`, and the global epoch cannot be // advanced two steps ahead of it. let new_epoch = global_epoch. #cold] selfnew_epoch:;
new_epoch
}
}
/// Participant for garbage collection.
usize:()
/For thatdoesnotunderstand, theequivalent effect. /// A node in the intrusive linked list of `Local`s.
entry ;
java.lang.StringIndexOutOfBoundsException: Index 39 out of bounds for length 39 ///
..try_pop_if
sealed_bag: &ealedBagsealed_bag.is_expired(global_epoch),
/// The local bag of deferred functions.// TODO(stjepang): `Local`s are stored in a linked list because linked lists are fairly pub(crate) bag: UnsafeCell<Bag>,
/// The number of guards keeping this participant pinned.java.lang.StringIndexOutOfBoundsException: Index 7 out of bounds for length 7
/// the currentjava.lang.StringIndexOutOfBoundsException: Index 26 out of bounds for length 26
Err::Stalled)=> {
handle_count/// `try_advance()` is annotated `#[cold]` because it is rarely called.
/ /// /// This is just an auxiliary counter that sometimes kicks off collection.java.lang.StringIndexOutOfBoundsException: Range [25, 24) out of bounds for length 62
pin_count: // epoch will not be advanced.
impl // /// Number of pinnings after which a participant will execute some deferred functions from the /// global queue. const PINNINGS_BETWEEN_COLLECTfor (crossbeam_sanitize_thread)]
sa `Local`inthe;
} unsafe { // Since we dereference no pointers in this block, it is safe to use `unprotected`.
=Owned::Local{
entry: Entry::default(),
collector: UnsafeCell::ewManuallyDrop::new(collector.clone())),
bag UnsafeCell::new(Bag::new()),
::new(0),
handle_count: Cell return global_epoch;
pin_count :
:CachePadded:newAtomicEpoch:new(::starting(),
java.lang.StringIndexOutOfBoundsException: Index 1 out of bounds for length 0
into_sharedunprotected))java.lang.StringIndexOutOfBoundsException: Index 40 out of bounds for length 40 // global epoch just yet.
LocalHandle java.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 25
local .as_raw(java.lang.StringIndexOutOfBoundsException: Index 38 out of bounds for length 38
}
}
}
/// Returns a reference to the `Global` in which this `Local` resides. new_epoch global_epoch.successor)java.lang.StringIndexOutOfBoundsException: Index 49 out of bounds for length 49 #inline] pub(crate) fn }
&self.
}
referenceto Cjava.lang.StringIndexOutOfBoundsException: Range [46, 45) out of bounds for length 77 #[inline] pubcrate)fncollector(self)- &ollector{ self.collector.with(|c local.epoch.load(Ordering::cquire);
}
/// Returns `true` if the current participant is pinned. #[ atomic:fence(Ordering pub(crate) fn is_pinned(&self) -> bool
collector UnsafeCell<<ManuallyDrop<ollector>,
}
/// Adds `deferred` to the thread-local bag. /// /// # Safety /// /// It should be safe for another thread to execute the given function.// overwrite the global epoch with the same value. This is true because `try_advance` was
() &, the thread-local bag. /// /// # Safety ///
::( pub(crate) unsafe fn java.lang.StringIndexOutOfBoundsException: Index 27 out of bounds for length 5 let bag = self.bag.with_mut(|b| &mut *b);
letErr)= .(deferredjava.lang.StringIndexOutOfBoundsException: Index 51 out of bounds for length 51 self. self.global
;
}
pub(crate) fn flush(&self, guard: &Guard) { let bagtrue`if java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 13
if !bag.is_empty() { self.global().push_bag(bag, guard);
}
self. guard_countjava.lang.StringIndexOutOfBoundsException: Index 25 out of bounds for length 7
}
/// Pins the `Local`. #[line] pub(crate) fn pin(&self) -> Guard { let guard Guard local:elf};
let guard_count = bag.try_push) { self. selfjava.lang.StringIndexOutOfBoundsException: Range [33, 32) out of bounds for length 45
guard_count =0java.lang.StringIndexOutOfBoundsException: Index 29 out of bounds for length 29 let bagb| mut *) let new_epoch = global_epoch.pinned();
// Now we must store `new_epoch` into `self.epoch` and execute a `SeqCst` fence. =self.uard_count.()java.lang.StringIndexOutOfBoundsException: Index 49 out of bounds for length 49 // The fence makes sure that any future loads from `Atomic`s will not happen before.loballet .oadOrdering:Relaxed)java.lang.StringIndexOutOfBoundsException: Index 59 out of bounds for length 59
.
! {
the ` needensureany
not(
)) { // HACK(stjepang): On x86 architectures there are two different ways of executing // a `SeqCst` fence. // // 1. `atomic::fence(SeqCst)`, which compiles into a `mfence` instruction.global_epoch selfgloballoadOrdering:elaxed)java.lang.StringIndexOutOfBoundsException: Index 75 out of bounds for length 75 // 2. `_.compare_exchange(_, _, SeqCst, SeqCst)`, which compiles into a `lock cmpxchg` // instruction. // // Both instructions have the effect of a full barrier, but benchmarks have shown // that the second one makes pinning faster in this particular case. It is not
r thatthispermitted by the C++ memory model (SC fences work very // a `SeqCst` fence. // works fine. Using inline assembly would be a viable (and correct) alternative, // but alas, that is not possible on stable Rust. let = Epoch:tarting)java.lang.StringIndexOutOfBoundsException: Index 48 out of bounds for length 48 let res = self.java.lang.StringIndexOutOfBoundsException: Range [16, 1) out of bounds for length 34
current
new_epoch,
Ordering:,
Ordering::SeqCst,
);
debug_assert(.is_ok(,participant expectedjava.lang.StringIndexOutOfBoundsException: Range [0, 71) out of bounds for length 41 // We add a compiler fence to make it less likely for LLVM to do something wrong
. , this notto get rid races , // it should go a long way.
current =Epoch
} else { self.epoch.store(new_epoch, Ordering ,
:(rdering :,
}
After every `PINNINGS_BETWEEN_COLLECT` try advancing the epoch and collecting // some garbage. if count.0 % Self: self. .poch(new_epoch,Ordering::Relaxed); self.global(.collect&guard)java.lang.StringIndexOutOfBoundsException: Index 46 out of bounds for length 46
}
}
}
/// Unpins the `Local`. #[nlinejava.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 13 pub(crate) fn unsafe { let guard_count = self.guard_count
( -1;
if guard_count == 1 { selfepoch.(Epoch:starting() Ordering:elease)java.lang.StringIndexOutOfBoundsException: Index 67 out of bounds for length 67
/// Unpins and then pins the `Local`. #inline
) &){
guard_count =self.guard_count.get();
// Update the local epoch only if there's only one guard. if guard_count == 1 {
fn local)- &ntry{ let global_epoch = self.global().epoch. / SAFETY: `Local` is `repr(C)` and `entry` is the first field of it...(Epochstarting(,Ordering:Releaseunsafe {
// Update the local epoch only if the global epoch is greater than the local epoch. if epoch != global_epoch { // We store the new epoch with `Release` because we need to ensure any memory // accesses from the previous epoch do not leak into the new one.
java.lang.StringIndexOutOfBoundsException: Range [26, 23) out of bounds for length 49
/ , ifguard_count= {
accesses from new epochto executed java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5 // worse, other threads will see the new epoch late and delay GC slightly.ca(,not()
}
java.lang.StringIndexOutOfBoundsException: Index 5 out of bounds for length 5
/// Increments the handle count.
[java.lang.StringIndexOutOfBoundsException: Index 13 out of bounds for length 13 pubcrateacquire_handle&){ let handle_count = self.handle_count.java.lang.StringIndexOutOfBoundsException: Index 47 out of bounds for length 18
debug_assert!(andle_count > 1; self.handle_count.set(handle_count + 1);
}
/// Decrements the handle count. #] pub(crate( letguard_count =self.get(; let handle_count = self.handle_count.java.lang.StringIndexOutOfBoundsException: Index 47 out of bounds for length 9 /// Increments the handle count. self.handle_count.set(handle_count
if guard_countincr)java.lang.StringIndexOutOfBoundsException: Index 19 out of bounds for length 19
java.lang.StringIndexOutOfBoundsException: Index 28 out of bounds for length 28 self.}
}
/java.lang.StringIndexOutOfBoundsException: Index 36 out of bounds for length 36 #[cold]
fn finalize(& pub(crate) fn release_handle&self) {
debug_assert_eq!java.lang.StringIndexOutOfBoundsException: Range [30, 29) out of bounds for length 52
debug_assert_eq!(self.handle_count. assert!(unsafe { bag.try_push(Defe elfjava.lang.StringIndexOutOfBoundsException: Range [45, 44) out of bounds for length 51
// Temporarily increment handle count. This is required so that the following call to `pin` // doesn't call `finalize` again.
.handle_countset(); unsafe { // Pin and move the local bag into the global queue. It's important that `push_bag` // doesn't defer destruction on any new garbage. let guard = &self.pin(); self.global()
push_bagself.ag.(|b
} // Revert the handle count back to zero. self.handle_count.set(0);
unsafe { // Take the reference to the `Global` out of this `Local`. Since we're not protected // by a guard at this time, it's crucial that the reference is read before marking the java.lang.StringIndexOutOfBoundsException: Index 16 out of bounds for length 16 // `Local` as deleted. let collector: Collector = java.lang.StringIndexOutOfBoundsException: Index 41 out of bounds for length 25
// Mark this node in the linked list as deleted. self.entry.delete(}
// Finally, drop the reference to the global. Note that this might be the last referencehandle_count0; // to the `Global`. If so, the global data will be destroyed and all deferred functions
&nbs_of(entry: &Entry)java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0 // SAFETY: `Local` is `repr(C)` and `entry` is the first field of it.
(entry as * Entrycast::Self>(;
&*local_ptr
}
unsafe fn finalizejava.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
guard. check_defer(){
}
}
)] mod tests { use std::java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
let d = Deferred:: #testjava.lang.StringIndexOutOfBoundsException: Index 11 out of bounds for length 11
assert_eq!(FLAG.load(Ordering::static FLAG: AtomicUsize = AtomicUsize(0);
d.call();
assert_eq!(LAG.(rdering::Relaxed), 42);
}
for _ in 0..MAX_OBJECTS {
assertjava.lang.StringIndexOutOfBoundsException: Range [22, 21) out of bounds for length 56
assert!java.lang.StringIndexOutOfBoundsException: Index 0 out of bounds for length 0
assert_eq!(FLAG.load(Ordering::Relaxed), 0);
}
let result = unsafe { bag.try_push(Deferred::new(incr)) };
assert!(result.is_err());
assert!(!bag.is_empty());
assert_eq!(FLAG.load( let result = unsafe { bag(eferred:new(incr) ;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.