[saferecl] # 32 Concurrency support library [[thread]](./#thread) ## 32.11 Safe reclamation [saferecl] ### [32.11.1](#general) General [[saferecl.general]](saferecl.general) [1](#general-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12912) Subclause [saferecl] contains safe-reclamation techniques, which are most frequently used to straightforwardly resolve access-deletion races[.](#general-1.sentence-1) ### [32.11.2](#rcu) Read-copy update (RCU) [[saferecl.rcu]](saferecl.rcu) #### [32.11.2.1](#rcu.general) General [[saferecl.rcu.general]](saferecl.rcu.general) [1](#rcu.general-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12920) RCU is a synchronization mechanism that can be used for linked data structures that are frequently read, but seldom updated[.](#rcu.general-1.sentence-1) RCU does not provide mutual exclusion, but instead allows the user to schedule specified actions such as deletion at some later time[.](#rcu.general-1.sentence-2) [2](#rcu.general-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12928) A class type T is [*rcu-protectable*](#def:rcu-protectable "32.11.2.1 General [saferecl.rcu.general]") if it has exactly one base class of type rcu_obj_base for some D, and that base is public and non-virtual, and it has no base classes of type rcu_obj_base for any other combination X, Y[.](#rcu.general-2.sentence-1) An object is rcu-protectable if it is of rcu-protectable type[.](#rcu.general-2.sentence-2) [3](#rcu.general-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12936) An invocation of unlock U on an rcu_domain dom corresponds to an invocation of lock L on dom if L is sequenced before U and either - [(3.1)](#rcu.general-3.1) no other invocation of lock on dom is sequenced after L and before U, or - [(3.2)](#rcu.general-3.2) every invocation of unlock U2 on dom such that L is sequenced before U2 and U2 is sequenced before U corresponds to an invocation of lock L2 on dom such that L is sequenced before L2 and L2 is sequenced before U2[.](#rcu.general-3.sentence-1) [*Note [1](#rcu.general-note-1)*: This pairs nested locks and unlocks on a given domain in each thread[.](#rcu.general-3.sentence-2) — *end note*] [4](#rcu.general-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12954) A [*region of RCU protection*](#def:region_of_RCU_protection "32.11.2.1 General [saferecl.rcu.general]") on a domain dom starts with a lock L on dom and ends with its corresponding unlock U[.](#rcu.general-4.sentence-1) [5](#rcu.general-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12959) Given a region of RCU protection R on a domain dom and given an evaluation E that scheduled another evaluation F in dom, if E does not strongly happen before the start of R, the end of R strongly happens before evaluating F[.](#rcu.general-5.sentence-1) [6](#rcu.general-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12965) The evaluation of a scheduled evaluation is potentially concurrent with any other scheduled evaluation[.](#rcu.general-6.sentence-1) Each scheduled evaluation is evaluated at most once[.](#rcu.general-6.sentence-2) #### [32.11.2.2](#rcu.syn) Header synopsis [[rcu.syn]](rcu.syn) [🔗](#header:%3crcu%3e) namespace std {// [[saferecl.rcu.base]](#rcu.base "32.11.2.3 Class template rcu_­obj_­base"), class template rcu_obj_basetemplate> class rcu_obj_base; // [[saferecl.rcu.domain]](#rcu.domain "32.11.2.4 Class rcu_­domain"), class rcu_domainclass rcu_domain; // [[saferecl.rcu.domain.func]](#rcu.domain.func "32.11.2.4.3 Non-member functions"), non-member functions rcu_domain& rcu_default_domain() noexcept; void rcu_synchronize(rcu_domain& dom = rcu_default_domain()) noexcept; void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept; template>void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());} #### [32.11.2.3](#rcu.base) Class template rcu_obj_base [[saferecl.rcu.base]](saferecl.rcu.base) [1](#rcu.base-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12992) Objects of type T to be protected by RCU inherit from a specialization rcu_obj_base for some D[.](#rcu.base-1.sentence-1) namespace std {template>class rcu_obj_base {public:void retire(D d = D(), rcu_domain& dom = rcu_default_domain()) noexcept; protected: rcu_obj_base() = default; rcu_obj_base(const rcu_obj_base&) = default; rcu_obj_base(rcu_obj_base&&) = default; rcu_obj_base& operator=(const rcu_obj_base&) = default; rcu_obj_base& operator=(rcu_obj_base&&) = default; ~rcu_obj_base() = default; private: D *deleter*; // *exposition only*};} [2](#rcu.base-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13015) The behavior of a program that adds specializations for rcu_obj_base is undefined[.](#rcu.base-2.sentence-1) [3](#rcu.base-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13019) T may be an incomplete type[.](#rcu.base-3.sentence-1) It shall be complete before any member of the resulting specialization ofrcu_obj_base is referenced[.](#rcu.base-3.sentence-2) [4](#rcu.base-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13024) D shall be a function object type ([[function.objects]](function.objects "22.10 Function objects")) for which, given a value d of type D and a value ptr of type T*, the expression d(ptr) is valid[.](#rcu.base-4.sentence-1) [5](#rcu.base-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13031) D shall meet the requirements for[*Cpp17DefaultConstructible*](utility.arg.requirements#:Cpp17DefaultConstructible "16.4.4.2 Template argument requirements [utility.arg.requirements]") and [*Cpp17MoveAssignable*](utility.arg.requirements#:Cpp17MoveAssignable "16.4.4.2 Template argument requirements [utility.arg.requirements]")[.](#rcu.base-5.sentence-1) [6](#rcu.base-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13035) If D is trivially copyable, all specializations of rcu_obj_base are trivially copyable[.](#rcu.base-6.sentence-1) [🔗](#rcu.base-itemdecl:1) `void retire(D d = D(), rcu_domain& dom = rcu_default_domain()) noexcept; ` [7](#rcu.base-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13044) *Mandates*: T is an rcu-protectable type[.](#rcu.base-7.sentence-1) [8](#rcu.base-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13048) *Preconditions*: *this is a base class subobject of an object x of type T[.](#rcu.base-8.sentence-1) The member function rcu_obj_base​::​retire was not invoked on x before[.](#rcu.base-8.sentence-2) The assignment to *deleter* does not exit via an exception[.](#rcu.base-8.sentence-3) [9](#rcu.base-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13056) *Effects*: Evaluates *deleter* = std​::​move(d) and schedules the evaluation of the expression *deleter*( addressof(x)) in the domain dom; the behavior is undefined if that evaluation exits via an exception[.](#rcu.base-9.sentence-1) May invoke scheduled evaluations in dom[.](#rcu.base-9.sentence-2) [*Note [1](#rcu.base-note-1)*: If such evaluations acquire resources held across any invocation ofretire on dom, deadlock can occur[.](#rcu.base-9.sentence-3) — *end note*] #### [32.11.2.4](#rcu.domain) Class rcu_domain [[saferecl.rcu.domain]](saferecl.rcu.domain) #### [32.11.2.4.1](#rcu.domain.general) General [[saferecl.rcu.domain.general]](saferecl.rcu.domain.general) namespace std {class rcu_domain {public: rcu_domain(const rcu_domain&) = delete; rcu_domain& operator=(const rcu_domain&) = delete; void lock() noexcept; bool try_lock() noexcept; void unlock() noexcept; };} [1](#rcu.domain.general-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13089) This class meets the requirements of[*Cpp17Lockable*](thread.req.lockable.req#:Cpp17Lockable "32.2.5.3 Cpp17Lockable requirements [thread.req.lockable.req]") ([[thread.req.lockable.req]](thread.req.lockable.req "32.2.5.3 Cpp17Lockable requirements")) and provides regions of RCU protection[.](#rcu.domain.general-1.sentence-1) [*Example [1](#rcu.domain.general-example-1)*: std::scoped_lock rlock(rcu_default_domain()); — *end example*] [2](#rcu.domain.general-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13099) The functions lock and unlock establish (possibly nested) regions of RCU protection[.](#rcu.domain.general-2.sentence-1) #### [32.11.2.4.2](#rcu.domain.members) Member functions [[saferecl.rcu.domain.members]](saferecl.rcu.domain.members) [🔗](#lib:lock,rcu_domain) `void lock() noexcept; ` [1](#rcu.domain.members-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13111) *Effects*: Opens a region of RCU protection[.](#rcu.domain.members-1.sentence-1) [2](#rcu.domain.members-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13115) *Remarks*: Calls to lock do not introduce a data race ([[intro.races]](intro.races "6.10.2.2 Data races")) involving *this[.](#rcu.domain.members-2.sentence-1) [🔗](#lib:try_lock,rcu_domain) `bool try_lock() noexcept; ` [3](#rcu.domain.members-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13127) *Effects*: Equivalent to lock()[.](#rcu.domain.members-3.sentence-1) [4](#rcu.domain.members-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13131) *Returns*: true[.](#rcu.domain.members-4.sentence-1) [🔗](#lib:unlock,rcu_domain) `void unlock() noexcept; ` [5](#rcu.domain.members-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13142) *Preconditions*: A call to lock that opened an unclosed region of RCU protection is sequenced before the call to unlock[.](#rcu.domain.members-5.sentence-1) [6](#rcu.domain.members-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13148) *Effects*: Closes the unclosed region of RCU protection that was most recently opened[.](#rcu.domain.members-6.sentence-1) May invoke scheduled evaluations in *this[.](#rcu.domain.members-6.sentence-2) [7](#rcu.domain.members-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13154) [*Note [1](#rcu.domain.members-note-1)*: If such evaluations acquire resources held across any invocation of unlock on *this, deadlock can occur[.](#rcu.domain.members-7.sentence-1) — *end note*] [8](#rcu.domain.members-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13161) *Remarks*: Calls to unlock do not introduce a data race involving *this[.](#rcu.domain.members-8.sentence-1) [*Note [2](#rcu.domain.members-note-2)*: Evaluation of scheduled evaluations can still cause a data race[.](#rcu.domain.members-8.sentence-2) — *end note*] #### [32.11.2.4.3](#rcu.domain.func) Non-member functions [[saferecl.rcu.domain.func]](saferecl.rcu.domain.func) [🔗](#lib:rcu_default_domain) `rcu_domain& rcu_default_domain() noexcept; ` [1](#rcu.domain.func-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13177) *Returns*: A reference to a static-duration object of type rcu_domain[.](#rcu.domain.func-1.sentence-1) A reference to the same object is returned every time this function is called[.](#rcu.domain.func-1.sentence-2) [🔗](#lib:rcu_synchronize) `void rcu_synchronize(rcu_domain& dom = rcu_default_domain()) noexcept; ` [2](#rcu.domain.func-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13189) *Effects*: If the call to rcu_synchronize does not strongly happen before the lock opening an RCU protection region R on dom, blocks until the unlock closing R happens[.](#rcu.domain.func-2.sentence-1) [3](#rcu.domain.func-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13195) *Synchronization*: The unlock closing R strongly happens before the return from rcu_synchronize[.](#rcu.domain.func-3.sentence-1) [🔗](#lib:rcu_barrier) `void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept; ` [4](#rcu.domain.func-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13207) *Effects*: May evaluate any scheduled evaluations in dom[.](#rcu.domain.func-4.sentence-1) For any evaluation that happens before the call to rcu_barrier and that schedules an evaluation E in dom, blocks until E has been evaluated[.](#rcu.domain.func-4.sentence-2) [5](#rcu.domain.func-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13214) *Synchronization*: The evaluation of any such E strongly happens before the return from rcu_barrier[.](#rcu.domain.func-5.sentence-1) [*Note [1](#rcu.domain.func-note-1)*: A call to rcu_barrier does not imply a call to rcu_synchronize and vice versa[.](#rcu.domain.func-5.sentence-2) — *end note*] [🔗](#lib:rcu_retire) `template> void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain()); ` [6](#rcu.domain.func-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13232) *Mandates*: is_move_constructible_v is true and the expression d(p) is well-formed[.](#rcu.domain.func-6.sentence-1) [7](#rcu.domain.func-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13237) *Preconditions*: D meets the [*Cpp17MoveConstructible*](utility.arg.requirements#:Cpp17MoveConstructible "16.4.4.2 Template argument requirements [utility.arg.requirements]") and[*Cpp17Destructible*](utility.arg.requirements#:Cpp17Destructible "16.4.4.2 Template argument requirements [utility.arg.requirements]") requirements[.](#rcu.domain.func-7.sentence-1) [8](#rcu.domain.func-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13242) *Effects*: May allocate memory[.](#rcu.domain.func-8.sentence-1) It is unspecified whether the memory allocation is performed by invoking operator new[.](#rcu.domain.func-8.sentence-2) Initializes an object d1 of type D from std​::​move(d)[.](#rcu.domain.func-8.sentence-3) Schedules the evaluation of d1(p) in the domain dom; the behavior is undefined if that evaluation exits via an exception[.](#rcu.domain.func-8.sentence-4) May invoke scheduled evaluations in dom[.](#rcu.domain.func-8.sentence-5) [*Note [2](#rcu.domain.func-note-2)*: If rcu_retire exits via an exception, no evaluation is scheduled[.](#rcu.domain.func-8.sentence-6) — *end note*] [9](#rcu.domain.func-9) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13256) *Throws*: bad_alloc or any exception thrown by the initialization of d1[.](#rcu.domain.func-9.sentence-1) [10](#rcu.domain.func-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13260) [*Note [3](#rcu.domain.func-note-3)*: If scheduled evaluations acquire resources held across any invocation of rcu_retire on dom, deadlock can occur[.](#rcu.domain.func-10.sentence-1) — *end note*] ### [32.11.3](#hp) Hazard pointers [[saferecl.hp]](saferecl.hp) #### [32.11.3.1](#hp.general) General [[saferecl.hp.general]](saferecl.hp.general) [1](#hp.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[.](#hp.general-1.sentence-1) Only the owner of the hazard pointer can set its value, while any number of threads may read its value[.](#hp.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[.](#hp.general-1.sentence-3) [2](#hp.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.1 General [saferecl.hp.general]") if it has exactly one base class of type hazard_pointer_obj_base for some D, that base is public and non-virtual, and it has no base classes of type hazard_pointer_obj_base for any other combination T2, D2[.](#hp.general-2.sentence-1) An object is [*hazard-protectable*](#def:hazard-protectable "32.11.3.1 General [saferecl.hp.general]") if it is of hazard-protectable type[.](#hp.general-2.sentence-2) [3](#hp.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.1 General [saferecl.hp.general]"); in each protection epoch,h either is [*associated with*](#def:hazard_pointer,associated "32.11.3.1 General [saferecl.hp.general]") a hazard-protectable object, or is [*unassociated*](#def:hazard_pointer,unassociated "32.11.3.1 General [saferecl.hp.general]")[.](#hp.general-3.sentence-1) Upon creation, a hazard pointer is unassociated[.](#hp.general-3.sentence-2) Changing the association (possibly to the same object) initiates a new protection epoch and ends the preceding one[.](#hp.general-3.sentence-3) [4](#hp.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.1 General [saferecl.hp.general]") with a deleter of type D when the member function hazard_pointer_obj_base​::​retire is invoked on x[.](#hp.general-4.sentence-1) Any given object x shall be retired at most once[.](#hp.general-4.sentence-2) [5](#hp.general-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13309) A retired object x is [*reclaimed*](#def:reclaimed "32.11.3.1 General [saferecl.hp.general]") by invoking its deleter with a pointer to x; the behavior is undefined if that invocation exits via an exception[.](#hp.general-5.sentence-1) [6](#hp.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.1 General [saferecl.hp.general]") with respect to an evaluation A if - [(6.1)](#hp.general-6.1) x is not reclaimed; and - [(6.2)](#hp.general-6.2) x is retired in an evaluation R andA does not happen before R; and - [(6.3)](#hp.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)](#hp.general-6.3.1) if the beginning of E happens before R, the end of E strongly happens before A; and * [(6.3.2)](#hp.general-6.3.2) if E began by an evaluation of try_protect with argument src, label its atomic load operation L[.](#hp.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[.](#hp.general-6.3.2.sentence-2) [*Note [1](#hp.general-note-1)*: In typical use, a store to src sequenced before retiring x will be such an atomic operation B[.](#hp.general-6.3.2.sentence-3) — *end note*] [*Note [2](#hp.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[.](#hp.general-6.3.sentence-2) — *end note*] [7](#hp.general-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13351) The number of possibly-reclaimable objects has an unspecified bound[.](#hp.general-7.sentence-1) [*Note [3](#hp.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[.](#hp.general-7.sentence-2) — *end note*] [*Example [1](#hp.general-example-1)*: The following example shows how hazard pointers allow updates to be carried out in the presence of concurrent readers[.](#hp.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[.](#hp.general-7.sentence-4) struct Name : public hazard_pointer_obj_base { /* details */ }; atomic 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 synopsis [[hazard.pointer.syn]](hazard.pointer.syn) [🔗](#header:%3chazard_pointer%3e) namespace std {// [[saferecl.hp.base]](#hp.base "32.11.3.3 Class template hazard_­pointer_­obj_­base"), class template hazard_pointer_obj_basetemplate> class hazard_pointer_obj_base; // [[saferecl.hp.holder]](#hp.holder "32.11.3.4 Class hazard_­pointer"), class hazard_pointerclass hazard_pointer; // [[saferecl.hp.holder.nonmem]](#hp.holder.nonmem "32.11.3.4.4 Non-member functions"), non-member functions hazard_pointer make_hazard_pointer(); void swap(hazard_pointer&, hazard_pointer&) noexcept;} #### [32.11.3.3](#hp.base) Class template hazard_pointer_obj_base [[saferecl.hp.base]](saferecl.hp.base) namespace std {template>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](#hp.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.4 Requirements")) for which, given a value d of type D and a value ptr of type T*, the expression d(ptr) is valid[.](#hp.base-1.sentence-1) [2](#hp.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[.](#hp.base-2.sentence-1) [3](#hp.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.2 Template argument requirements [utility.arg.requirements]") and [*Cpp17MoveAssignable*](utility.arg.requirements#:Cpp17MoveAssignable "16.4.4.2 Template argument requirements [utility.arg.requirements]")[.](#hp.base-3.sentence-1) [4](#hp.base-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13434) T may be an incomplete type[.](#hp.base-4.sentence-1) It shall be complete before any member of the resulting specialization of hazard_pointer_obj_base is referenced[.](#hp.base-4.sentence-2) [🔗](#lib:retire,hazard_pointer_obj_base) `void retire(D d = D()) noexcept; ` [5](#hp.base-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13446) *Mandates*: T is a hazard-protectable type[.](#hp.base-5.sentence-1) [6](#hp.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[.](#hp.base-6.sentence-1) x is not retired[.](#hp.base-6.sentence-2) Move-assigning d to deleter does not exit via an exception[.](#hp.base-6.sentence-3) [7](#hp.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[.](#hp.base-7.sentence-1) May reclaim possibly-reclaimable objects[.](#hp.base-7.sentence-2) #### [32.11.3.4](#hp.holder) Class hazard_pointer [[saferecl.hp.holder]](saferecl.hp.holder) #### [32.11.3.4.1](#hp.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 T* protect(const atomic& src) noexcept; template bool try_protect(T*& ptr, const atomic& src) noexcept; template void reset_protection(const T* ptr) noexcept; void reset_protection(nullptr_t = nullptr) noexcept; void swap(hazard_pointer&) noexcept; };} [1](#hp.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.1 General [saferecl.hp.holder.general]") a hazard pointer[.](#hp.holder.general-1.sentence-1) Each hazard pointer is owned by exactly one object of type hazard_pointer[.](#hp.holder.general-1.sentence-2) [*Note [1](#hp.holder.general-note-1)*: An empty hazard_pointer object is different from a hazard_pointer object that owns an unassociated hazard pointer[.](#hp.holder.general-1.sentence-3) An empty hazard_pointer object does not own any hazard pointers[.](#hp.holder.general-1.sentence-4) — *end note*] #### [32.11.3.4.2](#hp.holder.ctor) Constructors, destructor, and assignment [[saferecl.hp.holder.ctor]](saferecl.hp.holder.ctor) [🔗](#lib:hazard_pointer,constructor) `hazard_pointer() noexcept; ` [1](#hp.holder.ctor-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13508) *Postconditions*: *this is empty[.](#hp.holder.ctor-1.sentence-1) [🔗](#lib:hazard_pointer,constructor_) `hazard_pointer(hazard_pointer&& other) noexcept; ` [2](#hp.holder.ctor-2) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13519) *Postconditions*: If other is empty, *this is empty[.](#hp.holder.ctor-2.sentence-1) Otherwise,*this owns the hazard pointer originally owned by other;other is empty[.](#hp.holder.ctor-2.sentence-2) [🔗](#lib:hazard_pointer,destructor) `~hazard_pointer(); ` [3](#hp.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[.](#hp.holder.ctor-3.sentence-1) [🔗](#lib:operator=,hazard_pointer) `hazard_pointer& operator=(hazard_pointer&& other) noexcept; ` [4](#hp.holder.ctor-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13546) *Effects*: If this == &other is true, no effect[.](#hp.holder.ctor-4.sentence-1) Otherwise, if *this is not empty, destroys the hazard pointer owned by *this, thereby ending its current protection epoch[.](#hp.holder.ctor-4.sentence-2) [5](#hp.holder.ctor-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13553) *Postconditions*: If other was empty, *this is empty[.](#hp.holder.ctor-5.sentence-1) Otherwise, *this owns the hazard pointer originally owned by other[.](#hp.holder.ctor-5.sentence-2) If this != &other is true, other is empty[.](#hp.holder.ctor-5.sentence-3) [6](#hp.holder.ctor-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13560) *Returns*: *this[.](#hp.holder.ctor-6.sentence-1) #### [32.11.3.4.3](#hp.holder.mem) Member functions [[saferecl.hp.holder.mem]](saferecl.hp.holder.mem) [🔗](#lib:empty,hazard_pointer) `bool empty() const noexcept; ` [1](#hp.holder.mem-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13573) *Returns*: true if and only if *this is empty[.](#hp.holder.mem-1.sentence-1) [🔗](#lib:protect,hazard_pointer) `template T* protect(const atomic& src) noexcept; ` [2](#hp.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 bool try_protect(T*& ptr, const atomic& src) noexcept; ` [3](#hp.holder.mem-3) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13600) *Mandates*: T is a hazard-protectable type[.](#hp.holder.mem-3.sentence-1) [4](#hp.holder.mem-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13604) *Preconditions*: *this is not empty[.](#hp.holder.mem-4.sentence-1) [5](#hp.holder.mem-5) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13608) *Effects*: Performs the following steps in order: - [(5.1)](#hp.holder.mem-5.1) Initializes a variable old of type T* with the value of ptr[.](#hp.holder.mem-5.1.sentence-1) - [(5.2)](#hp.holder.mem-5.2) Evaluates reset_protection(old)[.](#hp.holder.mem-5.2.sentence-1) - [(5.3)](#hp.holder.mem-5.3) Assigns the value of src.load(memory_order​::​acquire) to ptr[.](#hp.holder.mem-5.3.sentence-1) - [(5.4)](#hp.holder.mem-5.4) If old == ptr is false, evaluates reset_protection()[.](#hp.holder.mem-5.4.sentence-1) [6](#hp.holder.mem-6) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13619) *Returns*: old == ptr[.](#hp.holder.mem-6.sentence-1) [🔗](#lib:reset_protection,hazard_pointer) `template void reset_protection(const T* ptr) noexcept; ` [7](#hp.holder.mem-7) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13630) *Mandates*: T is a hazard-protectable type[.](#hp.holder.mem-7.sentence-1) [8](#hp.holder.mem-8) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13634) *Preconditions*: *this is not empty[.](#hp.holder.mem-8.sentence-1) [9](#hp.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()[.](#hp.holder.mem-9.sentence-1) Otherwise, associates the hazard pointer owned by *this with *ptr, thereby ending the current protection epoch[.](#hp.holder.mem-9.sentence-2) [10](#hp.holder.mem-10) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13645) *Complexity*: Constant[.](#hp.holder.mem-10.sentence-1) [🔗](#lib:reset_protection,hazard_pointer_) `void reset_protection(nullptr_t = nullptr) noexcept; ` [11](#hp.holder.mem-11) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13656) *Preconditions*: *this is not empty[.](#hp.holder.mem-11.sentence-1) [12](#hp.holder.mem-12) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13660) *Postconditions*: The hazard pointer owned by *this is unassociated[.](#hp.holder.mem-12.sentence-1) [13](#hp.holder.mem-13) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13664) *Complexity*: Constant[.](#hp.holder.mem-13.sentence-1) [🔗](#lib:swap,hazard_pointer) `void swap(hazard_pointer& other) noexcept; ` [14](#hp.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[.](#hp.holder.mem-14.sentence-1) [*Note [1](#hp.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[.](#hp.holder.mem-14.sentence-2) No protection epochs are ended or initiated[.](#hp.holder.mem-14.sentence-3) — *end note*] [15](#hp.holder.mem-15) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13685) *Complexity*: Constant[.](#hp.holder.mem-15.sentence-1) #### [32.11.3.4.4](#hp.holder.nonmem) Non-member functions [[saferecl.hp.holder.nonmem]](saferecl.hp.holder.nonmem) [🔗](#lib:make_hazard_pointer) `hazard_pointer make_hazard_pointer(); ` [1](#hp.holder.nonmem-1) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13698) *Effects*: Constructs a hazard pointer[.](#hp.holder.nonmem-1.sentence-1) [2](#hp.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[.](#hp.holder.nonmem-2.sentence-1) [3](#hp.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[.](#hp.holder.nonmem-3.sentence-1) [🔗](#lib:swap,hazard_pointer_) `void swap(hazard_pointer& a, hazard_pointer& b) noexcept; ` [4](#hp.holder.nonmem-4) [#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13718) *Effects*: Equivalent to a.swap(b)[.](#hp.holder.nonmem-4.sentence-1)