Files
2025-10-25 03:02:53 +03:00

492 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[saferecl.hp]
# 32 Concurrency support library [[thread]](./#thread)
## 32.11 Safe reclamation [[saferecl]](saferecl#hp)
### 32.11.3 Hazard pointers [saferecl.hp]
#### [32.11.3.1](#general) General [[saferecl.hp.general]](saferecl.hp.general)
[1](#general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13272)
A hazard pointer is a single-writer multi-reader pointer
that can be owned by at most one thread at any time[.](#general-1.sentence-1)
Only the owner of the hazard pointer can set its value,
while any number of threads may read its value[.](#general-1.sentence-2)
The owner thread sets the value of a hazard pointer to point to an object
in order to indicate to concurrent threads—which
may delete such an object—that
the object is not yet safe to delete[.](#general-1.sentence-3)
[2](#general-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13283)
A class type T is [*hazard-protectable*](#def:hazard-protectable "32.11.3.1General[saferecl.hp.general]") if it has exactly one base class of type hazard_pointer_obj_base<T, D> for some D,
that base is public and non-virtual, and
it has no base classes of type hazard_pointer_obj_base<T2, D2> for any other combination T2, D2[.](#general-2.sentence-1)
An object is [*hazard-protectable*](#def:hazard-protectable "32.11.3.1General[saferecl.hp.general]") if it is of hazard-protectable type[.](#general-2.sentence-2)
[3](#general-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13292)
The time span between creation and destruction of a hazard pointer h is partitioned into a series of [*protection epochs*](#def:epoch,protection "32.11.3.1General[saferecl.hp.general]");
in each protection epoch,h either is [*associated with*](#def:hazard_pointer,associated "32.11.3.1General[saferecl.hp.general]") a hazard-protectable object, or is [*unassociated*](#def:hazard_pointer,unassociated "32.11.3.1General[saferecl.hp.general]")[.](#general-3.sentence-1)
Upon creation, a hazard pointer is unassociated[.](#general-3.sentence-2)
Changing the association (possibly to the same object)
initiates a new protection epoch and ends the preceding one[.](#general-3.sentence-3)
[4](#general-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13302)
An object x of hazard-protectable type T is[*retired*](#def:retired "32.11.3.1General[saferecl.hp.general]") with a deleter of type D when the member function hazard_pointer_obj_base<T, D>::retire is invoked on x[.](#general-4.sentence-1)
Any given object x shall be retired at most once[.](#general-4.sentence-2)
[5](#general-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13309)
A retired object x is [*reclaimed*](#def:reclaimed "32.11.3.1General[saferecl.hp.general]") by invoking its deleter with a pointer to x;
the behavior is undefined if that invocation exits via an exception[.](#general-5.sentence-1)
[6](#general-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13314)
A hazard-protectable object x is [*possibly-reclaimable*](#def:possibly-reclaimable "32.11.3.1General[saferecl.hp.general]") with respect to an evaluation A if
- [(6.1)](#general-6.1)
x is not reclaimed; and
- [(6.2)](#general-6.2)
x is retired in an evaluation R andA does not happen before R; and
- [(6.3)](#general-6.3)
for all hazard pointers h and for every protection epoch E of h during which h is associated with x:
* [(6.3.1)](#general-6.3.1)
if the beginning of E happens before R,
the end of E strongly happens before A; and
* [(6.3.2)](#general-6.3.2)
if E began by an evaluation of try_protect with argument src,
label its atomic load operation L[.](#general-6.sentence-1)
If there exists an atomic modification B on src such that L observes a modification that is modification-ordered before B, andB happens before x is retired,
the end of E strongly happens before A[.](#general-6.3.2.sentence-2)
[*Note [1](#general-note-1)*:
In typical use, a store to src sequenced before retiring x will be such an atomic operation B[.](#general-6.3.2.sentence-3)
— *end note*]
[*Note [2](#general-note-2)*:
The latter two conditions convey the informal notion
that a protection epoch that began before retiring x,
as implied either by the happens-before relation or
the coherence order of some source,
delays the reclamation of x[.](#general-6.3.sentence-2)
— *end note*]
[7](#general-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13351)
The number of possibly-reclaimable objects has an unspecified bound[.](#general-7.sentence-1)
[*Note [3](#general-note-3)*:
The bound can be a function of the number of hazard pointers,
the number of threads that retire objects, and
the number of threads that use hazard pointers[.](#general-7.sentence-2)
— *end note*]
[*Example [1](#general-example-1)*:
The following example shows how hazard pointers allow updates to be carried out
in the presence of concurrent readers[.](#general-7.sentence-3)
The object of type hazard_pointer in print_name protects the object *ptr from being reclaimed by ptr->retire until the end of the protection epoch[.](#general-7.sentence-4)
struct Name : public hazard_pointer_obj_base<Name> { /* details */ };
atomic<Name*> name;// called often and in parallel!void print_name() { hazard_pointer h = make_hazard_pointer();
Name* ptr = h.protect(name); // Protection epoch starts// ... safe to access *ptr} // Protection epoch ends.// called rarely, but possibly concurrently with print_namevoid update_name(Name* new_name) { Name* ptr = name.exchange(new_name);
ptr->retire();} — *end example*]
#### [32.11.3.2](#hazard.pointer.syn) Header <hazard_pointer> synopsis [[hazard.pointer.syn]](hazard.pointer.syn)
[🔗](#header:%3chazard_pointer%3e)
namespace std {// [[saferecl.hp.base]](#base "32.11.3.3Class template hazard_­pointer_­obj_­base"), class template hazard_pointer_obj_basetemplate<class T, class D = default_delete<T>> class hazard_pointer_obj_base; // [[saferecl.hp.holder]](#holder "32.11.3.4Class hazard_­pointer"), class hazard_pointerclass hazard_pointer; // [[saferecl.hp.holder.nonmem]](#holder.nonmem "32.11.3.4.4Non-member functions"), non-member functions hazard_pointer make_hazard_pointer(); void swap(hazard_pointer&, hazard_pointer&) noexcept;}
#### [32.11.3.3](#base) Class template hazard_pointer_obj_base [[saferecl.hp.base]](saferecl.hp.base)
namespace std {template<class T, class D = default_delete<T>>class hazard_pointer_obj_base {public:void retire(D d = D()) noexcept; protected: hazard_pointer_obj_base() = default;
hazard_pointer_obj_base(const hazard_pointer_obj_base&) = default;
hazard_pointer_obj_base(hazard_pointer_obj_base&&) = default;
hazard_pointer_obj_base& operator=(const hazard_pointer_obj_base&) = default;
hazard_pointer_obj_base& operator=(hazard_pointer_obj_base&&) = default; ~hazard_pointer_obj_base() = default; private: D *deleter*; // *exposition only*};}
[1](#base-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13420)
D shall be a function object type ([[func.require]](func.require "22.10.4Requirements"))
for which, given a value d of type D and
a value ptr of type T*,
the expression d(ptr) is valid[.](#base-1.sentence-1)
[2](#base-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13426)
The behavior of a program
that adds specializations for hazard_pointer_obj_base is undefined[.](#base-2.sentence-1)
[3](#base-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13430)
D shall meet the requirements for[*Cpp17DefaultConstructible*](utility.arg.requirements#:Cpp17DefaultConstructible "16.4.4.2Template argument requirements[utility.arg.requirements]") and [*Cpp17MoveAssignable*](utility.arg.requirements#:Cpp17MoveAssignable "16.4.4.2Template argument requirements[utility.arg.requirements]")[.](#base-3.sentence-1)
[4](#base-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13434)
T may be an incomplete type[.](#base-4.sentence-1)
It shall be complete before any member
of the resulting specialization of hazard_pointer_obj_base is referenced[.](#base-4.sentence-2)
[🔗](#lib:retire,hazard_pointer_obj_base)
`void retire(D d = D()) noexcept;
`
[5](#base-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13446)
*Mandates*: T is a hazard-protectable type[.](#base-5.sentence-1)
[6](#base-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13450)
*Preconditions*: *this is
a base class subobject of an object x of type T[.](#base-6.sentence-1)
x is not retired[.](#base-6.sentence-2)
Move-assigning d to deleter does not exit via an exception[.](#base-6.sentence-3)
[7](#base-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13457)
*Effects*: Move-assigns d to deleter,
thereby setting it as the deleter of x,
then retires x[.](#base-7.sentence-1)
May reclaim possibly-reclaimable objects[.](#base-7.sentence-2)
#### [32.11.3.4](#holder) Class hazard_pointer [[saferecl.hp.holder]](saferecl.hp.holder)
#### [32.11.3.4.1](#holder.general) General [[saferecl.hp.holder.general]](saferecl.hp.holder.general)
namespace std {class hazard_pointer {public: hazard_pointer() noexcept;
hazard_pointer(hazard_pointer&&) noexcept;
hazard_pointer& operator=(hazard_pointer&&) noexcept; ~hazard_pointer(); bool empty() const noexcept; template<class T> T* protect(const atomic<T*>& src) noexcept; template<class T> bool try_protect(T*& ptr, const atomic<T*>& src) noexcept; template<class T> void reset_protection(const T* ptr) noexcept; void reset_protection(nullptr_t = nullptr) noexcept; void swap(hazard_pointer&) noexcept; };}
[1](#holder.general-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13488)
An object of type hazard_pointer is either empty or[*owns*](#def:owning,hazard_pointer "32.11.3.4.1General[saferecl.hp.holder.general]") a hazard pointer[.](#holder.general-1.sentence-1)
Each hazard pointer is owned by
exactly one object of type hazard_pointer[.](#holder.general-1.sentence-2)
[*Note [1](#holder.general-note-1)*:
An empty hazard_pointer object is different from
a hazard_pointer object
that owns an unassociated hazard pointer[.](#holder.general-1.sentence-3)
An empty hazard_pointer object does not own any hazard pointers[.](#holder.general-1.sentence-4)
— *end note*]
#### [32.11.3.4.2](#holder.ctor) Constructors, destructor, and assignment [[saferecl.hp.holder.ctor]](saferecl.hp.holder.ctor)
[🔗](#lib:hazard_pointer,constructor)
`hazard_pointer() noexcept;
`
[1](#holder.ctor-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13508)
*Postconditions*: *this is empty[.](#holder.ctor-1.sentence-1)
[🔗](#lib:hazard_pointer,constructor_)
`hazard_pointer(hazard_pointer&& other) noexcept;
`
[2](#holder.ctor-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13519)
*Postconditions*: If other is empty, *this is empty[.](#holder.ctor-2.sentence-1)
Otherwise,*this owns the hazard pointer originally owned by other;other is empty[.](#holder.ctor-2.sentence-2)
[🔗](#lib:hazard_pointer,destructor)
`~hazard_pointer();
`
[3](#holder.ctor-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13533)
*Effects*: If *this is not empty,
destroys the hazard pointer owned by *this,
thereby ending its current protection epoch[.](#holder.ctor-3.sentence-1)
[🔗](#lib:operator=,hazard_pointer)
`hazard_pointer& operator=(hazard_pointer&& other) noexcept;
`
[4](#holder.ctor-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13546)
*Effects*: If this == &other is true, no effect[.](#holder.ctor-4.sentence-1)
Otherwise, if *this is not empty,
destroys the hazard pointer owned by *this,
thereby ending its current protection epoch[.](#holder.ctor-4.sentence-2)
[5](#holder.ctor-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13553)
*Postconditions*: If other was empty, *this is empty[.](#holder.ctor-5.sentence-1)
Otherwise, *this owns the hazard pointer originally
owned by other[.](#holder.ctor-5.sentence-2)
If this != &other is true, other is empty[.](#holder.ctor-5.sentence-3)
[6](#holder.ctor-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13560)
*Returns*: *this[.](#holder.ctor-6.sentence-1)
#### [32.11.3.4.3](#holder.mem) Member functions [[saferecl.hp.holder.mem]](saferecl.hp.holder.mem)
[🔗](#lib:empty,hazard_pointer)
`bool empty() const noexcept;
`
[1](#holder.mem-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13573)
*Returns*: true if and only if *this is empty[.](#holder.mem-1.sentence-1)
[🔗](#lib:protect,hazard_pointer)
`template<class T> T* protect(const atomic<T*>& src) noexcept;
`
[2](#holder.mem-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13584)
*Effects*: Equivalent to:T* ptr = src.load(memory_order::relaxed);while (!try_protect(ptr, src)) {}return ptr;
[🔗](#lib:try_protect,hazard_pointer)
`template<class T> bool try_protect(T*& ptr, const atomic<T*>& src) noexcept;
`
[3](#holder.mem-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13600)
*Mandates*: T is a hazard-protectable type[.](#holder.mem-3.sentence-1)
[4](#holder.mem-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13604)
*Preconditions*: *this is not empty[.](#holder.mem-4.sentence-1)
[5](#holder.mem-5)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13608)
*Effects*: Performs the following steps in order:
- [(5.1)](#holder.mem-5.1)
Initializes a variable old of type T* with the value of ptr[.](#holder.mem-5.1.sentence-1)
- [(5.2)](#holder.mem-5.2)
Evaluates reset_protection(old)[.](#holder.mem-5.2.sentence-1)
- [(5.3)](#holder.mem-5.3)
Assigns the value of src.load(memory_order::acquire) to ptr[.](#holder.mem-5.3.sentence-1)
- [(5.4)](#holder.mem-5.4)
If old == ptr is false,
evaluates reset_protection()[.](#holder.mem-5.4.sentence-1)
[6](#holder.mem-6)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13619)
*Returns*: old == ptr[.](#holder.mem-6.sentence-1)
[🔗](#lib:reset_protection,hazard_pointer)
`template<class T> void reset_protection(const T* ptr) noexcept;
`
[7](#holder.mem-7)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13630)
*Mandates*: T is a hazard-protectable type[.](#holder.mem-7.sentence-1)
[8](#holder.mem-8)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13634)
*Preconditions*: *this is not empty[.](#holder.mem-8.sentence-1)
[9](#holder.mem-9)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13638)
*Effects*: If ptr is a null pointer value, invokes reset_protection()[.](#holder.mem-9.sentence-1)
Otherwise,
associates the hazard pointer owned by *this with *ptr,
thereby ending the current protection epoch[.](#holder.mem-9.sentence-2)
[10](#holder.mem-10)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13645)
*Complexity*: Constant[.](#holder.mem-10.sentence-1)
[🔗](#lib:reset_protection,hazard_pointer_)
`void reset_protection(nullptr_t = nullptr) noexcept;
`
[11](#holder.mem-11)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13656)
*Preconditions*: *this is not empty[.](#holder.mem-11.sentence-1)
[12](#holder.mem-12)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13660)
*Postconditions*: The hazard pointer owned by *this is unassociated[.](#holder.mem-12.sentence-1)
[13](#holder.mem-13)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13664)
*Complexity*: Constant[.](#holder.mem-13.sentence-1)
[🔗](#lib:swap,hazard_pointer)
`void swap(hazard_pointer& other) noexcept;
`
[14](#holder.mem-14)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13675)
*Effects*: Swaps the hazard pointer ownership of this object with that of other[.](#holder.mem-14.sentence-1)
[*Note [1](#holder.mem-note-1)*:
The owned hazard pointers, if any, remain unchanged during the swap and
continue to be associated with the respective objects
that they were protecting before the swap, if any[.](#holder.mem-14.sentence-2)
No protection epochs are ended or initiated[.](#holder.mem-14.sentence-3)
— *end note*]
[15](#holder.mem-15)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13685)
*Complexity*: Constant[.](#holder.mem-15.sentence-1)
#### [32.11.3.4.4](#holder.nonmem) Non-member functions [[saferecl.hp.holder.nonmem]](saferecl.hp.holder.nonmem)
[🔗](#lib:make_hazard_pointer)
`hazard_pointer make_hazard_pointer();
`
[1](#holder.nonmem-1)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13698)
*Effects*: Constructs a hazard pointer[.](#holder.nonmem-1.sentence-1)
[2](#holder.nonmem-2)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13702)
*Returns*: A hazard_pointer object that owns the newly-constructed hazard pointer[.](#holder.nonmem-2.sentence-1)
[3](#holder.nonmem-3)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13706)
*Throws*: May throw bad_alloc if memory for the hazard pointer could not be allocated[.](#holder.nonmem-3.sentence-1)
[🔗](#lib:swap,hazard_pointer_)
`void swap(hazard_pointer& a, hazard_pointer& b) noexcept;
`
[4](#holder.nonmem-4)
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13718)
*Effects*: Equivalent to a.swap(b)[.](#holder.nonmem-4.sentence-1)