Struct haphazard::Domain

source ·
pub struct Domain<F> { /* private fields */ }
Expand description

Synchronization point between hazard pointers and the writers they guard against.

Every hazard pointer is associated with a domain, and can only guard against reclamation of objects that are retired through that same domain. In other words, you should always ensure that your code uses the same domain to retire objects as it uses to make hazard pointers to read those objects. If it does not, the hazard pointers will provide no meaningful protection. This connection is part of the safety contract for HazardPointer::protect.

Domain families

To help aid in determining that the same domain is used for loads and stores, every domain has an associated domain family (F). The family serves no purpose beyond adding a statically checked guide so that obviously-incompatible domains aren’t used. To take advantage of it, your code should define a new zero-sized type that you use every F appears, like so:

#[non_exhaustive]
struct Family;

type Domain = haphazard::Domain<Family>;
type HazardPointer<'domain> = haphazard::HazardPointer<'domain, Family>;
type AtomicPtr<T> = haphazard::AtomicPtr<T, Family>;

This ensures at compile-time that you don’t, for example, use a HazardPointer from the global domain to guard loads from an AtomicPtr that is tied to a custom domain.

This isn’t bullet-proof though! Nothing prevents you from using hazard pointers allocated from one instance of Domain<Family> with an atomic pointer whose writers use a different instance of Domain<Family>. So be careful!

The unique_domain macro provides a mechanism for constructing a domain with a unique domain family that cannot be confused with any other. If you can use it, you should do so, as it gives stronger static guarantees. However, it has the downside that you cannot name the return type (at least without impl Trait in type aliases), which makes it difficult to store in other types.

In some cases, static_unique_domain can provide a convenient alternative. This macro makes it possible to declare a static domain with a namable family. This makes it possible to create additional static domains. Domains declared with this macro are always held by a static variable, which limits their usefulness somewhat, but it can allow more ergonomic use since HazardPointers and AtomicPtrs in this domain can be stored in structs. See the documentation for static_unique_domain for an example.

Reclamation

Domains are the coordination mechanism used for reclamation. When an object is retired into a domain, the retiring thread will (sometimes) scan the domain for objects that are now safe to reclaim (i.e., drop). Objects that cannot yet be reclaimed because there are active readers are left in the domain for a later retire to check again. This means that there is generally a delay between when an object is retired (i.e., marked as deleted) and when it is actually reclaimed (i.e., drop is called). And if there are no more retires, the objects may not be reclaimed until the owning domain is itself dropped.

When using the global domain (or a static_unique_domain) to guard data access in your data structure, keep in mind that there is no guarantee that retired objects will be cleaned up by the time your data structure is dropped. As a result, you may need to require that the data you store in said data structure be 'static. If you wish to avoid that bound, you’ll need to construct your own Domain for each instance of your data structure so that all the guarded data is reclaimed when your data structure is dropped.

Implementations§

source§

impl Domain<Global>

source

pub fn global() -> &'static Self

Get a handle to the singleton global domain.

source§

impl<F> Domain<F>

source

pub const fn new(_: &F) -> Self

Construct a new domain with the given family type.

The type checker protects you from accidentally using a HazardPointer from one domain family (the type F) with an object protected by a domain in a different family. However, it does not protect you from mixing up domains with the same family type. Therefore, prefer creating domains with unique_domain or static_unique_domain where possible, since they guarantee a unique F for every domain.

See the Domain documentation for more details.

source

pub unsafe fn retire_ptr<T, P>(&self, ptr: *mut T) -> usize
where T: Send, P: Pointer<T>,

Retire ptr, and reclaim it once it is safe to do so.

T must be Send since it may be reclaimed by a different thread.

Safety
  1. No HazardPointer will guard ptr from this point forward.
  2. ptr has not already been retired unless it has been reclaimed since then.
  3. ptr is valid as &T until self is dropped.
source

pub fn eager_reclaim(&self) -> usize

Reclaim as many retired objects as possible.

Returns the number of retired objects that were reclaimed.

Trait Implementations§

source§

impl<F> Drop for Domain<F>

source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

§

impl<F> RefUnwindSafe for Domain<F>
where F: RefUnwindSafe,

§

impl<F> Send for Domain<F>
where F: Send,

§

impl<F> Sync for Domain<F>
where F: Sync,

§

impl<F> Unpin for Domain<F>
where F: Unpin,

§

impl<F> !UnwindSafe for Domain<F>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> Reclaim for T