Skip to content

Commit

Permalink
rework guard API
Browse files Browse the repository at this point in the history
  • Loading branch information
ibraheemdev committed Apr 16, 2024
1 parent 1080851 commit 465d42b
Show file tree
Hide file tree
Showing 8 changed files with 327 additions and 199 deletions.
2 changes: 1 addition & 1 deletion benches/single_thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ criterion_main!(benches);

mod seize_stack {
use criterion::black_box;
use seize::{Collector, Link, Linked};
use seize::{Collector, Guard, Link, Linked};
use std::ptr;

pub struct Stack {
Expand Down
2 changes: 1 addition & 1 deletion benches/stack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ criterion_group!(benches, treiber_stack);
criterion_main!(benches);

mod seize_stack {
use seize::{reclaim, Collector, Linked};
use seize::{reclaim, Collector, Guard, Linked};
use std::mem::ManuallyDrop;
use std::ptr;
use std::sync::atomic::{AtomicPtr, Ordering};
Expand Down
170 changes: 14 additions & 156 deletions src/collector.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
use crate::raw;
use crate::tls::Thread;
use crate::{raw, LocalGuard, OwnedGuard};

use std::cell::UnsafeCell;
use std::marker::PhantomData;
use std::num::NonZeroU64;
use std::sync::atomic::{AtomicPtr, Ordering};
use std::sync::atomic::Ordering;
use std::{fmt, ptr};

/// Fast, efficient, and robust memory reclamation.
///
/// See the [crate documentation](crate) for details.
pub struct Collector {
raw: raw::Collector,
pub(crate) raw: raw::Collector,
unique: *mut u8,
}

Expand Down Expand Up @@ -129,15 +128,14 @@ impl Collector {
/// # unsafe { guard2.defer_retire(value, reclaim::boxed::<Linked<usize>>) };
/// drop(guard2) // _now_, the thread is marked as inactive

Check failure on line 129 in src/collector.rs

View workflow job for this annotation

GitHub Actions / Docs

no method named `defer_retire` found for struct `LocalGuard` in the current scope

Check failure on line 129 in src/collector.rs

View workflow job for this annotation

GitHub Actions / Test

no method named `defer_retire` found for struct `LocalGuard` in the current scope
/// ```
pub fn enter(&self) -> Guard<'_> {
let thread = Thread::current();
unsafe { self.raw.enter(thread) };

Guard {
thread,
collector: Some(self),
_unsend: PhantomData,
}
pub fn enter(&self) -> LocalGuard<'_> {
LocalGuard::enter(self)
}

/// Create an owned guard that protects objects for it's
/// lifetime, independent of the current thread.
pub fn enter_owned(&self) -> OwnedGuard<'_> {
OwnedGuard::enter(self)
}

/// Link a value to the collector.
Expand Down Expand Up @@ -202,14 +200,13 @@ impl Collector {
pub unsafe fn retire<T: AsLink>(&self, ptr: *mut T, reclaim: unsafe fn(*mut Link)) {
debug_assert!(!ptr.is_null(), "attempted to retire null pointer");

// note that `add` doesn't actually reclaim the pointer immediately if the
// current thread is active, it instead adds it to it's reclamation list,
// note that `add` doesn't ever actually reclaim the pointer immediately if
// the current thread is active, it instead adds it to it's reclamation list,
// but we don't guarantee that publicly.
unsafe { self.raw.add(ptr, reclaim, Thread::current()) }
}

/// Returns true if both references point to the same collector.
pub fn ptr_eq(this: &Collector, other: &Collector) -> bool {
pub(crate) fn ptr_eq(this: &Collector, other: &Collector) -> bool {
ptr::eq(this.unique, other.unique)
}
}
Expand Down Expand Up @@ -251,145 +248,6 @@ impl fmt::Debug for Collector {
}
}

/// A guard that keeps the current thread marked as active,
/// enabling protected loads of atomic pointers.
///
/// See [`Collector::enter`] for details.
pub struct Guard<'a> {
collector: Option<&'a Collector>,
thread: Thread,
// must not be Send or Sync as we are tied to the current threads state in
// the collector
_unsend: PhantomData<*mut ()>,
}

impl Guard<'_> {
/// Returns a dummy guard.
///
/// Calling [`protect`](Guard::protect) on an unprotected guard will
/// load the pointer directly, and [`retire`](Guard::defer_retire) will
/// reclaim objects immediately.
///
/// Unprotected guards are useful when calling guarded functions
/// on a data structure that has just been created or is about
/// to be destroyed, because you know that no other thread holds
/// a reference to it.
///
/// # Safety
///
/// You must ensure that code used with this guard is sound with
/// the unprotected behavior described above.
pub const unsafe fn unprotected() -> Guard<'static> {
Guard {
thread: Thread::EMPTY,
collector: None,
_unsend: PhantomData,
}
}

/// Protects the load of an atomic pointer.
///
/// See [the guide](crate#protecting-pointers) for details.
#[inline]
pub fn protect<T: AsLink>(&self, ptr: &AtomicPtr<T>, ordering: Ordering) -> *mut T {
match self.collector {
Some(collector) => unsafe { collector.raw.protect(ptr, ordering, self.thread) },
// unprotected guard
None => ptr.load(ordering),
}
}

/// Retires a value, running `reclaim` when no threads hold a reference to it.
///
/// This method delays reclamation until the guard is dropped as opposed to
/// [`Collector::retire`], which may reclaim objects immediately.
///
/// See [the guide](crate#retiring-objects) for details.
#[allow(clippy::missing_safety_doc)] // in guide
pub unsafe fn defer_retire<T: AsLink>(&self, ptr: *mut T, reclaim: unsafe fn(*mut Link)) {
debug_assert!(!ptr.is_null(), "attempted to retire null pointer");

match self.collector {
Some(collector) => unsafe { collector.raw.add(ptr, reclaim, self.thread) },
// unprotected guard
None => unsafe { (reclaim)(ptr.cast::<Link>()) },
}
}

/// Get a reference to the collector this guard we created from.
///
/// This method is useful when you need to ensure that all guards
/// used with a data structure come from the same collector.
///
/// If this is an [`unprotected`](Guard::unprotected) guard
/// this method will return `None`.
pub fn collector(&self) -> Option<&Collector> {
self.collector
}

/// Refreshes the guard.
///
/// Refreshing a guard is similar to dropping and immediately
/// creating a new guard. The curent thread remains active, but any
/// pointers that were previously protected may be reclaimed.
///
/// # Safety
///
/// This method is not marked as `unsafe`, but will affect
/// the validity of pointers returned by [`protect`](Guard::protect),
/// similar to dropping a guard. It is intended to be used safely
/// by users of concurrent data structures, as references will
/// be tied to the guard and this method takes `&mut self`.
///
/// If this is an [`unprotected`](Guard::unprotected) guard
/// this method will be a no-op.
pub fn refresh(&mut self) {
match self.collector {
None => {}
Some(collector) => unsafe { collector.raw.refresh(self.thread) },
}
}

/// Flush any retired values in the local batch.
///
/// This method flushes any values from the current thread's local
/// batch, starting the reclamation process. Note that no memory
/// can be reclaimed while this guard is active, but calling `flush`
/// may allow memory to be reclaimed more quickly after the guard is
/// dropped.
///
/// See [`Collector::batch_size`] for details about batching.
pub fn flush(&self) {
if let Some(collector) = self.collector {
unsafe { collector.raw.try_retire_batch(self.thread) }
}
}

/// Returns a numeric identifier for the current thread.
///
/// Guards rely on thread-local state, including thread IDs. If you already
/// have a guard you can use this method to get a cheap identifier for the
/// current thread, avoiding TLS overhead. Note that thread IDs may be reused,
/// so the value returned is only unique for the lifetime of this thread.
pub fn thread_id(&self) -> usize {
self.thread.id
}
}

impl Drop for Guard<'_> {
fn drop(&mut self) {
if let Some(collector) = self.collector {
unsafe { collector.raw.leave(self.thread) }
}
}
}

impl fmt::Debug for Guard<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("Guard").finish()
}
}

/// A link to the collector.
///
/// See [the guide](crate#custom-reclaimers) for details.
Expand Down
Loading

0 comments on commit 465d42b

Please sign in to comment.