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
HazardPointer
s and AtomicPtr
s 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>
impl Domain<Global>
sourcepub fn global() -> &'static Self
pub fn global() -> &'static Self
Get a handle to the singleton global domain.
source§impl<F> Domain<F>
impl<F> Domain<F>
sourcepub const fn new(_: &F) -> Self
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.
sourcepub unsafe fn retire_ptr<T, P>(&self, ptr: *mut T) -> usize
pub unsafe fn retire_ptr<T, P>(&self, ptr: *mut T) -> usize
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
- No
HazardPointer
will guardptr
from this point forward. ptr
has not already been retired unless it has been reclaimed since then.ptr
is valid as&T
untilself
is dropped.
sourcepub fn eager_reclaim(&self) -> usize
pub fn eager_reclaim(&self) -> usize
Reclaim as many retired objects as possible.
Returns the number of retired objects that were reclaimed.