Init
This commit is contained in:
14
cppdraft/saferecl/general.md
Normal file
14
cppdraft/saferecl/general.md
Normal file
@@ -0,0 +1,14 @@
|
||||
[saferecl.general]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#general)
|
||||
|
||||
### 32.11.1 General [saferecl.general]
|
||||
|
||||
[1](#1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L12912)
|
||||
|
||||
Subclause [[saferecl]](saferecl "32.11 Safe reclamation") contains safe-reclamation techniques, which are most
|
||||
frequently used to straightforwardly resolve access-deletion races[.](#1.sentence-1)
|
||||
491
cppdraft/saferecl/hp.md
Normal file
491
cppdraft/saferecl/hp.md
Normal file
@@ -0,0 +1,491 @@
|
||||
[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.1 General [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.1 General [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.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]")[.](#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.1 General [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.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[.](#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.1 General [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.3 Class 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.4 Class hazard_pointer"), class hazard_pointerclass hazard_pointer; // [[saferecl.hp.holder.nonmem]](#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](#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.4 Requirements"))
|
||||
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.2 Template argument requirements [utility.arg.requirements]") and [*Cpp17MoveAssignable*](utility.arg.requirements#:Cpp17MoveAssignable "16.4.4.2 Template 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.1 General [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)
|
||||
78
cppdraft/saferecl/hp/base.md
Normal file
78
cppdraft/saferecl/hp/base.md
Normal file
@@ -0,0 +1,78 @@
|
||||
[saferecl.hp.base]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#hp.base)
|
||||
|
||||
### 32.11.3 Hazard pointers [[saferecl.hp]](saferecl.hp#base)
|
||||
|
||||
#### 32.11.3.3 Class template hazard_pointer_obj_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](#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[.](#1.sentence-1)
|
||||
|
||||
[2](#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[.](#2.sentence-1)
|
||||
|
||||
[3](#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]")[.](#3.sentence-1)
|
||||
|
||||
[4](#4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13434)
|
||||
|
||||
T may be an incomplete type[.](#4.sentence-1)
|
||||
|
||||
It shall be complete before any member
|
||||
of the resulting specialization of hazard_pointer_obj_base is referenced[.](#4.sentence-2)
|
||||
|
||||
[ð](#lib:retire,hazard_pointer_obj_base)
|
||||
|
||||
`void retire(D d = D()) noexcept;
|
||||
`
|
||||
|
||||
[5](#5)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13446)
|
||||
|
||||
*Mandates*: T is a hazard-protectable type[.](#5.sentence-1)
|
||||
|
||||
[6](#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[.](#6.sentence-1)
|
||||
|
||||
x is not retired[.](#6.sentence-2)
|
||||
|
||||
Move-assigning d to deleter does not exit via an exception[.](#6.sentence-3)
|
||||
|
||||
[7](#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[.](#7.sentence-1)
|
||||
|
||||
May reclaim possibly-reclaimable objects[.](#7.sentence-2)
|
||||
127
cppdraft/saferecl/hp/general.md
Normal file
127
cppdraft/saferecl/hp/general.md
Normal file
@@ -0,0 +1,127 @@
|
||||
[saferecl.hp.general]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#hp.general)
|
||||
|
||||
### 32.11.3 Hazard pointers [[saferecl.hp]](saferecl.hp#general)
|
||||
|
||||
#### 32.11.3.1 General [saferecl.hp.general]
|
||||
|
||||
[1](#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[.](#1.sentence-1)
|
||||
|
||||
Only the owner of the hazard pointer can set its value,
|
||||
while any number of threads may read its value[.](#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[.](#1.sentence-3)
|
||||
|
||||
[2](#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<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[.](#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[.](#2.sentence-2)
|
||||
|
||||
[3](#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]")[.](#3.sentence-1)
|
||||
|
||||
Upon creation, a hazard pointer is unassociated[.](#3.sentence-2)
|
||||
|
||||
Changing the association (possibly to the same object)
|
||||
initiates a new protection epoch and ends the preceding one[.](#3.sentence-3)
|
||||
|
||||
[4](#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<T, D>::retire is invoked on x[.](#4.sentence-1)
|
||||
|
||||
Any given object x shall be retired at most once[.](#4.sentence-2)
|
||||
|
||||
[5](#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[.](#5.sentence-1)
|
||||
|
||||
[6](#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)](#6.1)
|
||||
|
||||
x is not reclaimed; and
|
||||
|
||||
- [(6.2)](#6.2)
|
||||
|
||||
x is retired in an evaluation R andA does not happen before R; and
|
||||
|
||||
- [(6.3)](#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)](#6.3.1)
|
||||
|
||||
if the beginning of E happens before R,
|
||||
the end of E strongly happens before A; and
|
||||
|
||||
* [(6.3.2)](#6.3.2)
|
||||
|
||||
if E began by an evaluation of try_protect with argument src,
|
||||
label its atomic load operation L[.](#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[.](#6.3.2.sentence-2)
|
||||
[*Note [1](#note-1)*:
|
||||
In typical use, a store to src sequenced before retiring x will be such an atomic operation B[.](#6.3.2.sentence-3)
|
||||
â *end note*]
|
||||
|
||||
[*Note [2](#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[.](#6.3.sentence-2)
|
||||
â *end note*]
|
||||
|
||||
[7](#7)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13351)
|
||||
|
||||
The number of possibly-reclaimable objects has an unspecified bound[.](#7.sentence-1)
|
||||
|
||||
[*Note [3](#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[.](#7.sentence-2)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[*Example [1](#example-1)*:
|
||||
|
||||
The following example shows how hazard pointers allow updates to be carried out
|
||||
in the presence of concurrent readers[.](#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[.](#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*]
|
||||
294
cppdraft/saferecl/hp/holder.md
Normal file
294
cppdraft/saferecl/hp/holder.md
Normal file
@@ -0,0 +1,294 @@
|
||||
[saferecl.hp.holder]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#hp.holder)
|
||||
|
||||
### 32.11.3 Hazard pointers [[saferecl.hp]](saferecl.hp#holder)
|
||||
|
||||
#### 32.11.3.4 Class hazard_pointer [saferecl.hp.holder]
|
||||
|
||||
#### [32.11.3.4.1](#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](#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[.](#general-1.sentence-1)
|
||||
|
||||
Each hazard pointer is owned by
|
||||
exactly one object of type hazard_pointer[.](#general-1.sentence-2)
|
||||
|
||||
[*Note [1](#general-note-1)*:
|
||||
|
||||
An empty hazard_pointer object is different from
|
||||
a hazard_pointer object
|
||||
that owns an unassociated hazard pointer[.](#general-1.sentence-3)
|
||||
|
||||
An empty hazard_pointer object does not own any hazard pointers[.](#general-1.sentence-4)
|
||||
|
||||
â *end note*]
|
||||
|
||||
#### [32.11.3.4.2](#ctor) Constructors, destructor, and assignment [[saferecl.hp.holder.ctor]](saferecl.hp.holder.ctor)
|
||||
|
||||
[ð](#lib:hazard_pointer,constructor)
|
||||
|
||||
`hazard_pointer() noexcept;
|
||||
`
|
||||
|
||||
[1](#ctor-1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13508)
|
||||
|
||||
*Postconditions*: *this is empty[.](#ctor-1.sentence-1)
|
||||
|
||||
[ð](#lib:hazard_pointer,constructor_)
|
||||
|
||||
`hazard_pointer(hazard_pointer&& other) noexcept;
|
||||
`
|
||||
|
||||
[2](#ctor-2)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13519)
|
||||
|
||||
*Postconditions*: If other is empty, *this is empty[.](#ctor-2.sentence-1)
|
||||
|
||||
Otherwise,*this owns the hazard pointer originally owned by other;other is empty[.](#ctor-2.sentence-2)
|
||||
|
||||
[ð](#lib:hazard_pointer,destructor)
|
||||
|
||||
`~hazard_pointer();
|
||||
`
|
||||
|
||||
[3](#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[.](#ctor-3.sentence-1)
|
||||
|
||||
[ð](#lib:operator=,hazard_pointer)
|
||||
|
||||
`hazard_pointer& operator=(hazard_pointer&& other) noexcept;
|
||||
`
|
||||
|
||||
[4](#ctor-4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13546)
|
||||
|
||||
*Effects*: If this == &other is true, no effect[.](#ctor-4.sentence-1)
|
||||
|
||||
Otherwise, if *this is not empty,
|
||||
destroys the hazard pointer owned by *this,
|
||||
thereby ending its current protection epoch[.](#ctor-4.sentence-2)
|
||||
|
||||
[5](#ctor-5)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13553)
|
||||
|
||||
*Postconditions*: If other was empty, *this is empty[.](#ctor-5.sentence-1)
|
||||
|
||||
Otherwise, *this owns the hazard pointer originally
|
||||
owned by other[.](#ctor-5.sentence-2)
|
||||
|
||||
If this != &other is true, other is empty[.](#ctor-5.sentence-3)
|
||||
|
||||
[6](#ctor-6)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13560)
|
||||
|
||||
*Returns*: *this[.](#ctor-6.sentence-1)
|
||||
|
||||
#### [32.11.3.4.3](#mem) Member functions [[saferecl.hp.holder.mem]](saferecl.hp.holder.mem)
|
||||
|
||||
[ð](#lib:empty,hazard_pointer)
|
||||
|
||||
`bool empty() const noexcept;
|
||||
`
|
||||
|
||||
[1](#mem-1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13573)
|
||||
|
||||
*Returns*: true if and only if *this is empty[.](#mem-1.sentence-1)
|
||||
|
||||
[ð](#lib:protect,hazard_pointer)
|
||||
|
||||
`template<class T> T* protect(const atomic<T*>& src) noexcept;
|
||||
`
|
||||
|
||||
[2](#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](#mem-3)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13600)
|
||||
|
||||
*Mandates*: T is a hazard-protectable type[.](#mem-3.sentence-1)
|
||||
|
||||
[4](#mem-4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13604)
|
||||
|
||||
*Preconditions*: *this is not empty[.](#mem-4.sentence-1)
|
||||
|
||||
[5](#mem-5)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13608)
|
||||
|
||||
*Effects*: Performs the following steps in order:
|
||||
|
||||
- [(5.1)](#mem-5.1)
|
||||
|
||||
Initializes a variable old of type T* with the value of ptr[.](#mem-5.1.sentence-1)
|
||||
|
||||
- [(5.2)](#mem-5.2)
|
||||
|
||||
Evaluates reset_protection(old)[.](#mem-5.2.sentence-1)
|
||||
|
||||
- [(5.3)](#mem-5.3)
|
||||
|
||||
Assigns the value of src.load(memory_order::acquire) to ptr[.](#mem-5.3.sentence-1)
|
||||
|
||||
- [(5.4)](#mem-5.4)
|
||||
|
||||
If old == ptr is false,
|
||||
evaluates reset_protection()[.](#mem-5.4.sentence-1)
|
||||
|
||||
[6](#mem-6)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13619)
|
||||
|
||||
*Returns*: old == ptr[.](#mem-6.sentence-1)
|
||||
|
||||
[ð](#lib:reset_protection,hazard_pointer)
|
||||
|
||||
`template<class T> void reset_protection(const T* ptr) noexcept;
|
||||
`
|
||||
|
||||
[7](#mem-7)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13630)
|
||||
|
||||
*Mandates*: T is a hazard-protectable type[.](#mem-7.sentence-1)
|
||||
|
||||
[8](#mem-8)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13634)
|
||||
|
||||
*Preconditions*: *this is not empty[.](#mem-8.sentence-1)
|
||||
|
||||
[9](#mem-9)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13638)
|
||||
|
||||
*Effects*: If ptr is a null pointer value, invokes reset_protection()[.](#mem-9.sentence-1)
|
||||
|
||||
Otherwise,
|
||||
associates the hazard pointer owned by *this with *ptr,
|
||||
thereby ending the current protection epoch[.](#mem-9.sentence-2)
|
||||
|
||||
[10](#mem-10)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13645)
|
||||
|
||||
*Complexity*: Constant[.](#mem-10.sentence-1)
|
||||
|
||||
[ð](#lib:reset_protection,hazard_pointer_)
|
||||
|
||||
`void reset_protection(nullptr_t = nullptr) noexcept;
|
||||
`
|
||||
|
||||
[11](#mem-11)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13656)
|
||||
|
||||
*Preconditions*: *this is not empty[.](#mem-11.sentence-1)
|
||||
|
||||
[12](#mem-12)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13660)
|
||||
|
||||
*Postconditions*: The hazard pointer owned by *this is unassociated[.](#mem-12.sentence-1)
|
||||
|
||||
[13](#mem-13)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13664)
|
||||
|
||||
*Complexity*: Constant[.](#mem-13.sentence-1)
|
||||
|
||||
[ð](#lib:swap,hazard_pointer)
|
||||
|
||||
`void swap(hazard_pointer& other) noexcept;
|
||||
`
|
||||
|
||||
[14](#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[.](#mem-14.sentence-1)
|
||||
|
||||
[*Note [1](#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[.](#mem-14.sentence-2)
|
||||
|
||||
No protection epochs are ended or initiated[.](#mem-14.sentence-3)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[15](#mem-15)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13685)
|
||||
|
||||
*Complexity*: Constant[.](#mem-15.sentence-1)
|
||||
|
||||
#### [32.11.3.4.4](#nonmem) Non-member functions [[saferecl.hp.holder.nonmem]](saferecl.hp.holder.nonmem)
|
||||
|
||||
[ð](#lib:make_hazard_pointer)
|
||||
|
||||
`hazard_pointer make_hazard_pointer();
|
||||
`
|
||||
|
||||
[1](#nonmem-1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13698)
|
||||
|
||||
*Effects*: Constructs a hazard pointer[.](#nonmem-1.sentence-1)
|
||||
|
||||
[2](#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[.](#nonmem-2.sentence-1)
|
||||
|
||||
[3](#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[.](#nonmem-3.sentence-1)
|
||||
|
||||
[ð](#lib:swap,hazard_pointer_)
|
||||
|
||||
`void swap(hazard_pointer& a, hazard_pointer& b) noexcept;
|
||||
`
|
||||
|
||||
[4](#nonmem-4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13718)
|
||||
|
||||
*Effects*: Equivalent to a.swap(b)[.](#nonmem-4.sentence-1)
|
||||
80
cppdraft/saferecl/hp/holder/ctor.md
Normal file
80
cppdraft/saferecl/hp/holder/ctor.md
Normal file
@@ -0,0 +1,80 @@
|
||||
[saferecl.hp.holder.ctor]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#hp.holder.ctor)
|
||||
|
||||
### 32.11.3 Hazard pointers [[saferecl.hp]](saferecl.hp#holder.ctor)
|
||||
|
||||
#### 32.11.3.4 Class hazard_pointer [[saferecl.hp.holder]](saferecl.hp.holder#ctor)
|
||||
|
||||
#### 32.11.3.4.2 Constructors, destructor, and assignment [saferecl.hp.holder.ctor]
|
||||
|
||||
[ð](#lib:hazard_pointer,constructor)
|
||||
|
||||
`hazard_pointer() noexcept;
|
||||
`
|
||||
|
||||
[1](#1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13508)
|
||||
|
||||
*Postconditions*: *this is empty[.](#1.sentence-1)
|
||||
|
||||
[ð](#lib:hazard_pointer,constructor_)
|
||||
|
||||
`hazard_pointer(hazard_pointer&& other) noexcept;
|
||||
`
|
||||
|
||||
[2](#2)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13519)
|
||||
|
||||
*Postconditions*: If other is empty, *this is empty[.](#2.sentence-1)
|
||||
|
||||
Otherwise,*this owns the hazard pointer originally owned by other;other is empty[.](#2.sentence-2)
|
||||
|
||||
[ð](#lib:hazard_pointer,destructor)
|
||||
|
||||
`~hazard_pointer();
|
||||
`
|
||||
|
||||
[3](#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[.](#3.sentence-1)
|
||||
|
||||
[ð](#lib:operator=,hazard_pointer)
|
||||
|
||||
`hazard_pointer& operator=(hazard_pointer&& other) noexcept;
|
||||
`
|
||||
|
||||
[4](#4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13546)
|
||||
|
||||
*Effects*: If this == &other is true, no effect[.](#4.sentence-1)
|
||||
|
||||
Otherwise, if *this is not empty,
|
||||
destroys the hazard pointer owned by *this,
|
||||
thereby ending its current protection epoch[.](#4.sentence-2)
|
||||
|
||||
[5](#5)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13553)
|
||||
|
||||
*Postconditions*: If other was empty, *this is empty[.](#5.sentence-1)
|
||||
|
||||
Otherwise, *this owns the hazard pointer originally
|
||||
owned by other[.](#5.sentence-2)
|
||||
|
||||
If this != &other is true, other is empty[.](#5.sentence-3)
|
||||
|
||||
[6](#6)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13560)
|
||||
|
||||
*Returns*: *this[.](#6.sentence-1)
|
||||
34
cppdraft/saferecl/hp/holder/general.md
Normal file
34
cppdraft/saferecl/hp/holder/general.md
Normal file
@@ -0,0 +1,34 @@
|
||||
[saferecl.hp.holder.general]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#hp.holder.general)
|
||||
|
||||
### 32.11.3 Hazard pointers [[saferecl.hp]](saferecl.hp#holder.general)
|
||||
|
||||
#### 32.11.3.4 Class hazard_pointer [[saferecl.hp.holder]](saferecl.hp.holder#general)
|
||||
|
||||
#### 32.11.3.4.1 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](#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[.](#1.sentence-1)
|
||||
|
||||
Each hazard pointer is owned by
|
||||
exactly one object of type hazard_pointer[.](#1.sentence-2)
|
||||
|
||||
[*Note [1](#note-1)*:
|
||||
|
||||
An empty hazard_pointer object is different from
|
||||
a hazard_pointer object
|
||||
that owns an unassociated hazard pointer[.](#1.sentence-3)
|
||||
|
||||
An empty hazard_pointer object does not own any hazard pointers[.](#1.sentence-4)
|
||||
|
||||
â *end note*]
|
||||
162
cppdraft/saferecl/hp/holder/mem.md
Normal file
162
cppdraft/saferecl/hp/holder/mem.md
Normal file
@@ -0,0 +1,162 @@
|
||||
[saferecl.hp.holder.mem]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#hp.holder.mem)
|
||||
|
||||
### 32.11.3 Hazard pointers [[saferecl.hp]](saferecl.hp#holder.mem)
|
||||
|
||||
#### 32.11.3.4 Class hazard_pointer [[saferecl.hp.holder]](saferecl.hp.holder#mem)
|
||||
|
||||
#### 32.11.3.4.3 Member functions [saferecl.hp.holder.mem]
|
||||
|
||||
[ð](#lib:empty,hazard_pointer)
|
||||
|
||||
`bool empty() const noexcept;
|
||||
`
|
||||
|
||||
[1](#1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13573)
|
||||
|
||||
*Returns*: true if and only if *this is empty[.](#1.sentence-1)
|
||||
|
||||
[ð](#lib:protect,hazard_pointer)
|
||||
|
||||
`template<class T> T* protect(const atomic<T*>& src) noexcept;
|
||||
`
|
||||
|
||||
[2](#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](#3)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13600)
|
||||
|
||||
*Mandates*: T is a hazard-protectable type[.](#3.sentence-1)
|
||||
|
||||
[4](#4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13604)
|
||||
|
||||
*Preconditions*: *this is not empty[.](#4.sentence-1)
|
||||
|
||||
[5](#5)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13608)
|
||||
|
||||
*Effects*: Performs the following steps in order:
|
||||
|
||||
- [(5.1)](#5.1)
|
||||
|
||||
Initializes a variable old of type T* with the value of ptr[.](#5.1.sentence-1)
|
||||
|
||||
- [(5.2)](#5.2)
|
||||
|
||||
Evaluates reset_protection(old)[.](#5.2.sentence-1)
|
||||
|
||||
- [(5.3)](#5.3)
|
||||
|
||||
Assigns the value of src.load(memory_order::acquire) to ptr[.](#5.3.sentence-1)
|
||||
|
||||
- [(5.4)](#5.4)
|
||||
|
||||
If old == ptr is false,
|
||||
evaluates reset_protection()[.](#5.4.sentence-1)
|
||||
|
||||
[6](#6)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13619)
|
||||
|
||||
*Returns*: old == ptr[.](#6.sentence-1)
|
||||
|
||||
[ð](#lib:reset_protection,hazard_pointer)
|
||||
|
||||
`template<class T> void reset_protection(const T* ptr) noexcept;
|
||||
`
|
||||
|
||||
[7](#7)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13630)
|
||||
|
||||
*Mandates*: T is a hazard-protectable type[.](#7.sentence-1)
|
||||
|
||||
[8](#8)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13634)
|
||||
|
||||
*Preconditions*: *this is not empty[.](#8.sentence-1)
|
||||
|
||||
[9](#9)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13638)
|
||||
|
||||
*Effects*: If ptr is a null pointer value, invokes reset_protection()[.](#9.sentence-1)
|
||||
|
||||
Otherwise,
|
||||
associates the hazard pointer owned by *this with *ptr,
|
||||
thereby ending the current protection epoch[.](#9.sentence-2)
|
||||
|
||||
[10](#10)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13645)
|
||||
|
||||
*Complexity*: Constant[.](#10.sentence-1)
|
||||
|
||||
[ð](#lib:reset_protection,hazard_pointer_)
|
||||
|
||||
`void reset_protection(nullptr_t = nullptr) noexcept;
|
||||
`
|
||||
|
||||
[11](#11)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13656)
|
||||
|
||||
*Preconditions*: *this is not empty[.](#11.sentence-1)
|
||||
|
||||
[12](#12)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13660)
|
||||
|
||||
*Postconditions*: The hazard pointer owned by *this is unassociated[.](#12.sentence-1)
|
||||
|
||||
[13](#13)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13664)
|
||||
|
||||
*Complexity*: Constant[.](#13.sentence-1)
|
||||
|
||||
[ð](#lib:swap,hazard_pointer)
|
||||
|
||||
`void swap(hazard_pointer& other) noexcept;
|
||||
`
|
||||
|
||||
[14](#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[.](#14.sentence-1)
|
||||
|
||||
[*Note [1](#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[.](#14.sentence-2)
|
||||
|
||||
No protection epochs are ended or initiated[.](#14.sentence-3)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[15](#15)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13685)
|
||||
|
||||
*Complexity*: Constant[.](#15.sentence-1)
|
||||
45
cppdraft/saferecl/hp/holder/nonmem.md
Normal file
45
cppdraft/saferecl/hp/holder/nonmem.md
Normal file
@@ -0,0 +1,45 @@
|
||||
[saferecl.hp.holder.nonmem]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#hp.holder.nonmem)
|
||||
|
||||
### 32.11.3 Hazard pointers [[saferecl.hp]](saferecl.hp#holder.nonmem)
|
||||
|
||||
#### 32.11.3.4 Class hazard_pointer [[saferecl.hp.holder]](saferecl.hp.holder#nonmem)
|
||||
|
||||
#### 32.11.3.4.4 Non-member functions [saferecl.hp.holder.nonmem]
|
||||
|
||||
[ð](#lib:make_hazard_pointer)
|
||||
|
||||
`hazard_pointer make_hazard_pointer();
|
||||
`
|
||||
|
||||
[1](#1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13698)
|
||||
|
||||
*Effects*: Constructs a hazard pointer[.](#1.sentence-1)
|
||||
|
||||
[2](#2)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13702)
|
||||
|
||||
*Returns*: A hazard_pointer object that owns the newly-constructed hazard pointer[.](#2.sentence-1)
|
||||
|
||||
[3](#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[.](#3.sentence-1)
|
||||
|
||||
[ð](#lib:swap,hazard_pointer)
|
||||
|
||||
`void swap(hazard_pointer& a, hazard_pointer& b) noexcept;
|
||||
`
|
||||
|
||||
[4](#4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13718)
|
||||
|
||||
*Effects*: Equivalent to a.swap(b)[.](#4.sentence-1)
|
||||
399
cppdraft/saferecl/rcu.md
Normal file
399
cppdraft/saferecl/rcu.md
Normal file
@@ -0,0 +1,399 @@
|
||||
[saferecl.rcu]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#rcu)
|
||||
|
||||
### 32.11.2 Read-copy update (RCU) [saferecl.rcu]
|
||||
|
||||
#### [32.11.2.1](#general) General [[saferecl.rcu.general]](saferecl.rcu.general)
|
||||
|
||||
[1](#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[.](#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[.](#general-1.sentence-2)
|
||||
|
||||
[2](#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<T, D> for some D, and that base is public and non-virtual, and
|
||||
it has no base classes of type rcu_obj_base<X, Y> for any other combination X, Y[.](#general-2.sentence-1)
|
||||
|
||||
An object is rcu-protectable if it is of rcu-protectable type[.](#general-2.sentence-2)
|
||||
|
||||
[3](#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)](#general-3.1)
|
||||
|
||||
no other invocation of lock on dom is sequenced after L and before U, or
|
||||
|
||||
- [(3.2)](#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[.](#general-3.sentence-1)
|
||||
|
||||
[*Note [1](#general-note-1)*:
|
||||
|
||||
This pairs nested locks and unlocks on a given domain in each thread[.](#general-3.sentence-2)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[4](#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[.](#general-4.sentence-1)
|
||||
|
||||
[5](#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[.](#general-5.sentence-1)
|
||||
|
||||
[6](#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[.](#general-6.sentence-1)
|
||||
|
||||
Each scheduled evaluation is evaluated at most once[.](#general-6.sentence-2)
|
||||
|
||||
#### [32.11.2.2](#rcu.syn) Header <rcu> synopsis [[rcu.syn]](rcu.syn)
|
||||
|
||||
[ð](#header:%3crcu%3e)
|
||||
|
||||
namespace std {// [[saferecl.rcu.base]](#base "32.11.2.3 Class template rcu_obj_base"), class template rcu_obj_basetemplate<class T, class D = default_delete<T>> class rcu_obj_base; // [[saferecl.rcu.domain]](#domain "32.11.2.4 Class rcu_domain"), class rcu_domainclass rcu_domain; // [[saferecl.rcu.domain.func]](#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<class T, class D = default_delete<T>>void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());}
|
||||
|
||||
#### [32.11.2.3](#base) Class template rcu_obj_base [[saferecl.rcu.base]](saferecl.rcu.base)
|
||||
|
||||
[1](#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<T, D> for some D[.](#base-1.sentence-1)
|
||||
|
||||
namespace std {template<class T, class D = default_delete<T>>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](#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[.](#base-2.sentence-1)
|
||||
|
||||
[3](#base-3)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13019)
|
||||
|
||||
T may be an incomplete type[.](#base-3.sentence-1)
|
||||
|
||||
It shall be complete before any member of the resulting specialization ofrcu_obj_base is referenced[.](#base-3.sentence-2)
|
||||
|
||||
[4](#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[.](#base-4.sentence-1)
|
||||
|
||||
[5](#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]")[.](#base-5.sentence-1)
|
||||
|
||||
[6](#base-6)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13035)
|
||||
|
||||
If D is trivially copyable,
|
||||
all specializations of rcu_obj_base<T, D> are trivially copyable[.](#base-6.sentence-1)
|
||||
|
||||
[ð](#base-itemdecl:1)
|
||||
|
||||
`void retire(D d = D(), rcu_domain& dom = rcu_default_domain()) noexcept;
|
||||
`
|
||||
|
||||
[7](#base-7)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13044)
|
||||
|
||||
*Mandates*: T is an rcu-protectable type[.](#base-7.sentence-1)
|
||||
|
||||
[8](#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[.](#base-8.sentence-1)
|
||||
|
||||
The member function rcu_obj_base<T, D>::retire was not invoked on x before[.](#base-8.sentence-2)
|
||||
|
||||
The assignment to *deleter* does not exit via an exception[.](#base-8.sentence-3)
|
||||
|
||||
[9](#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[.](#base-9.sentence-1)
|
||||
|
||||
May invoke scheduled evaluations in dom[.](#base-9.sentence-2)
|
||||
|
||||
[*Note [1](#base-note-1)*:
|
||||
|
||||
If such evaluations acquire resources held across any invocation ofretire on dom, deadlock can occur[.](#base-9.sentence-3)
|
||||
|
||||
â *end note*]
|
||||
|
||||
#### [32.11.2.4](#domain) Class rcu_domain [[saferecl.rcu.domain]](saferecl.rcu.domain)
|
||||
|
||||
#### [32.11.2.4.1](#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](#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[.](#domain.general-1.sentence-1)
|
||||
|
||||
[*Example [1](#domain.general-example-1)*: std::scoped_lock<rcu_domain> rlock(rcu_default_domain()); â *end example*]
|
||||
|
||||
[2](#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[.](#domain.general-2.sentence-1)
|
||||
|
||||
#### [32.11.2.4.2](#domain.members) Member functions [[saferecl.rcu.domain.members]](saferecl.rcu.domain.members)
|
||||
|
||||
[ð](#lib:lock,rcu_domain)
|
||||
|
||||
`void lock() noexcept;
|
||||
`
|
||||
|
||||
[1](#domain.members-1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13111)
|
||||
|
||||
*Effects*: Opens a region of RCU protection[.](#domain.members-1.sentence-1)
|
||||
|
||||
[2](#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[.](#domain.members-2.sentence-1)
|
||||
|
||||
[ð](#lib:try_lock,rcu_domain)
|
||||
|
||||
`bool try_lock() noexcept;
|
||||
`
|
||||
|
||||
[3](#domain.members-3)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13127)
|
||||
|
||||
*Effects*: Equivalent to lock()[.](#domain.members-3.sentence-1)
|
||||
|
||||
[4](#domain.members-4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13131)
|
||||
|
||||
*Returns*: true[.](#domain.members-4.sentence-1)
|
||||
|
||||
[ð](#lib:unlock,rcu_domain)
|
||||
|
||||
`void unlock() noexcept;
|
||||
`
|
||||
|
||||
[5](#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[.](#domain.members-5.sentence-1)
|
||||
|
||||
[6](#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[.](#domain.members-6.sentence-1)
|
||||
|
||||
May invoke scheduled evaluations in *this[.](#domain.members-6.sentence-2)
|
||||
|
||||
[7](#domain.members-7)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13154)
|
||||
|
||||
[*Note [1](#domain.members-note-1)*:
|
||||
|
||||
If such evaluations acquire resources
|
||||
held across any invocation of unlock on *this,
|
||||
deadlock can occur[.](#domain.members-7.sentence-1)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[8](#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[.](#domain.members-8.sentence-1)
|
||||
|
||||
[*Note [2](#domain.members-note-2)*:
|
||||
|
||||
Evaluation of scheduled evaluations can still cause a data race[.](#domain.members-8.sentence-2)
|
||||
|
||||
â *end note*]
|
||||
|
||||
#### [32.11.2.4.3](#domain.func) Non-member functions [[saferecl.rcu.domain.func]](saferecl.rcu.domain.func)
|
||||
|
||||
[ð](#lib:rcu_default_domain)
|
||||
|
||||
`rcu_domain& rcu_default_domain() noexcept;
|
||||
`
|
||||
|
||||
[1](#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[.](#domain.func-1.sentence-1)
|
||||
|
||||
A reference to the same object is returned every time this function is called[.](#domain.func-1.sentence-2)
|
||||
|
||||
[ð](#lib:rcu_synchronize)
|
||||
|
||||
`void rcu_synchronize(rcu_domain& dom = rcu_default_domain()) noexcept;
|
||||
`
|
||||
|
||||
[2](#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[.](#domain.func-2.sentence-1)
|
||||
|
||||
[3](#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[.](#domain.func-3.sentence-1)
|
||||
|
||||
[ð](#lib:rcu_barrier)
|
||||
|
||||
`void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept;
|
||||
`
|
||||
|
||||
[4](#domain.func-4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13207)
|
||||
|
||||
*Effects*: May evaluate any scheduled evaluations in dom[.](#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[.](#domain.func-4.sentence-2)
|
||||
|
||||
[5](#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[.](#domain.func-5.sentence-1)
|
||||
|
||||
[*Note [1](#domain.func-note-1)*:
|
||||
|
||||
A call to rcu_barrier does not imply
|
||||
a call to rcu_synchronize and vice versa[.](#domain.func-5.sentence-2)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[ð](#lib:rcu_retire)
|
||||
|
||||
`template<class T, class D = default_delete<T>>
|
||||
void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());
|
||||
`
|
||||
|
||||
[6](#domain.func-6)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13232)
|
||||
|
||||
*Mandates*: is_move_constructible_v<D> is true and
|
||||
the expression d(p) is well-formed[.](#domain.func-6.sentence-1)
|
||||
|
||||
[7](#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[.](#domain.func-7.sentence-1)
|
||||
|
||||
[8](#domain.func-8)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13242)
|
||||
|
||||
*Effects*: May allocate memory[.](#domain.func-8.sentence-1)
|
||||
|
||||
It is unspecified whether the memory allocation
|
||||
is performed by invoking operator new[.](#domain.func-8.sentence-2)
|
||||
|
||||
Initializes an object d1 of type D from std::move(d)[.](#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[.](#domain.func-8.sentence-4)
|
||||
|
||||
May invoke scheduled evaluations in dom[.](#domain.func-8.sentence-5)
|
||||
|
||||
[*Note [2](#domain.func-note-2)*:
|
||||
|
||||
If rcu_retire exits via an exception, no evaluation
|
||||
is scheduled[.](#domain.func-8.sentence-6)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[9](#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[.](#domain.func-9.sentence-1)
|
||||
|
||||
[10](#domain.func-10)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13260)
|
||||
|
||||
[*Note [3](#domain.func-note-3)*:
|
||||
|
||||
If scheduled evaluations acquire resources
|
||||
held across any invocation of rcu_retire on dom,
|
||||
deadlock can occur[.](#domain.func-10.sentence-1)
|
||||
|
||||
â *end note*]
|
||||
99
cppdraft/saferecl/rcu/base.md
Normal file
99
cppdraft/saferecl/rcu/base.md
Normal file
@@ -0,0 +1,99 @@
|
||||
[saferecl.rcu.base]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#rcu.base)
|
||||
|
||||
### 32.11.2 Read-copy update (RCU) [[saferecl.rcu]](saferecl.rcu#base)
|
||||
|
||||
#### 32.11.2.3 Class template rcu_obj_base [saferecl.rcu.base]
|
||||
|
||||
[1](#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<T, D> for some D[.](#1.sentence-1)
|
||||
|
||||
namespace std {template<class T, class D = default_delete<T>>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](#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[.](#2.sentence-1)
|
||||
|
||||
[3](#3)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13019)
|
||||
|
||||
T may be an incomplete type[.](#3.sentence-1)
|
||||
|
||||
It shall be complete before any member of the resulting specialization ofrcu_obj_base is referenced[.](#3.sentence-2)
|
||||
|
||||
[4](#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[.](#4.sentence-1)
|
||||
|
||||
[5](#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]")[.](#5.sentence-1)
|
||||
|
||||
[6](#6)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13035)
|
||||
|
||||
If D is trivially copyable,
|
||||
all specializations of rcu_obj_base<T, D> are trivially copyable[.](#6.sentence-1)
|
||||
|
||||
[ð](#itemdecl:1)
|
||||
|
||||
`void retire(D d = D(), rcu_domain& dom = rcu_default_domain()) noexcept;
|
||||
`
|
||||
|
||||
[7](#7)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13044)
|
||||
|
||||
*Mandates*: T is an rcu-protectable type[.](#7.sentence-1)
|
||||
|
||||
[8](#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[.](#8.sentence-1)
|
||||
|
||||
The member function rcu_obj_base<T, D>::retire was not invoked on x before[.](#8.sentence-2)
|
||||
|
||||
The assignment to *deleter* does not exit via an exception[.](#8.sentence-3)
|
||||
|
||||
[9](#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[.](#9.sentence-1)
|
||||
|
||||
May invoke scheduled evaluations in dom[.](#9.sentence-2)
|
||||
|
||||
[*Note [1](#note-1)*:
|
||||
|
||||
If such evaluations acquire resources held across any invocation ofretire on dom, deadlock can occur[.](#9.sentence-3)
|
||||
|
||||
â *end note*]
|
||||
233
cppdraft/saferecl/rcu/domain.md
Normal file
233
cppdraft/saferecl/rcu/domain.md
Normal file
@@ -0,0 +1,233 @@
|
||||
[saferecl.rcu.domain]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#rcu.domain)
|
||||
|
||||
### 32.11.2 Read-copy update (RCU) [[saferecl.rcu]](saferecl.rcu#domain)
|
||||
|
||||
#### 32.11.2.4 Class rcu_domain [saferecl.rcu.domain]
|
||||
|
||||
#### [32.11.2.4.1](#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](#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[.](#general-1.sentence-1)
|
||||
|
||||
[*Example [1](#general-example-1)*: std::scoped_lock<rcu_domain> rlock(rcu_default_domain()); â *end example*]
|
||||
|
||||
[2](#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[.](#general-2.sentence-1)
|
||||
|
||||
#### [32.11.2.4.2](#members) Member functions [[saferecl.rcu.domain.members]](saferecl.rcu.domain.members)
|
||||
|
||||
[ð](#lib:lock,rcu_domain)
|
||||
|
||||
`void lock() noexcept;
|
||||
`
|
||||
|
||||
[1](#members-1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13111)
|
||||
|
||||
*Effects*: Opens a region of RCU protection[.](#members-1.sentence-1)
|
||||
|
||||
[2](#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[.](#members-2.sentence-1)
|
||||
|
||||
[ð](#lib:try_lock,rcu_domain)
|
||||
|
||||
`bool try_lock() noexcept;
|
||||
`
|
||||
|
||||
[3](#members-3)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13127)
|
||||
|
||||
*Effects*: Equivalent to lock()[.](#members-3.sentence-1)
|
||||
|
||||
[4](#members-4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13131)
|
||||
|
||||
*Returns*: true[.](#members-4.sentence-1)
|
||||
|
||||
[ð](#lib:unlock,rcu_domain)
|
||||
|
||||
`void unlock() noexcept;
|
||||
`
|
||||
|
||||
[5](#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[.](#members-5.sentence-1)
|
||||
|
||||
[6](#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[.](#members-6.sentence-1)
|
||||
|
||||
May invoke scheduled evaluations in *this[.](#members-6.sentence-2)
|
||||
|
||||
[7](#members-7)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13154)
|
||||
|
||||
[*Note [1](#members-note-1)*:
|
||||
|
||||
If such evaluations acquire resources
|
||||
held across any invocation of unlock on *this,
|
||||
deadlock can occur[.](#members-7.sentence-1)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[8](#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[.](#members-8.sentence-1)
|
||||
|
||||
[*Note [2](#members-note-2)*:
|
||||
|
||||
Evaluation of scheduled evaluations can still cause a data race[.](#members-8.sentence-2)
|
||||
|
||||
â *end note*]
|
||||
|
||||
#### [32.11.2.4.3](#func) Non-member functions [[saferecl.rcu.domain.func]](saferecl.rcu.domain.func)
|
||||
|
||||
[ð](#lib:rcu_default_domain)
|
||||
|
||||
`rcu_domain& rcu_default_domain() noexcept;
|
||||
`
|
||||
|
||||
[1](#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[.](#func-1.sentence-1)
|
||||
|
||||
A reference to the same object is returned every time this function is called[.](#func-1.sentence-2)
|
||||
|
||||
[ð](#lib:rcu_synchronize)
|
||||
|
||||
`void rcu_synchronize(rcu_domain& dom = rcu_default_domain()) noexcept;
|
||||
`
|
||||
|
||||
[2](#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[.](#func-2.sentence-1)
|
||||
|
||||
[3](#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[.](#func-3.sentence-1)
|
||||
|
||||
[ð](#lib:rcu_barrier)
|
||||
|
||||
`void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept;
|
||||
`
|
||||
|
||||
[4](#func-4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13207)
|
||||
|
||||
*Effects*: May evaluate any scheduled evaluations in dom[.](#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[.](#func-4.sentence-2)
|
||||
|
||||
[5](#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[.](#func-5.sentence-1)
|
||||
|
||||
[*Note [1](#func-note-1)*:
|
||||
|
||||
A call to rcu_barrier does not imply
|
||||
a call to rcu_synchronize and vice versa[.](#func-5.sentence-2)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[ð](#lib:rcu_retire)
|
||||
|
||||
`template<class T, class D = default_delete<T>>
|
||||
void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());
|
||||
`
|
||||
|
||||
[6](#func-6)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13232)
|
||||
|
||||
*Mandates*: is_move_constructible_v<D> is true and
|
||||
the expression d(p) is well-formed[.](#func-6.sentence-1)
|
||||
|
||||
[7](#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[.](#func-7.sentence-1)
|
||||
|
||||
[8](#func-8)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13242)
|
||||
|
||||
*Effects*: May allocate memory[.](#func-8.sentence-1)
|
||||
|
||||
It is unspecified whether the memory allocation
|
||||
is performed by invoking operator new[.](#func-8.sentence-2)
|
||||
|
||||
Initializes an object d1 of type D from std::move(d)[.](#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[.](#func-8.sentence-4)
|
||||
|
||||
May invoke scheduled evaluations in dom[.](#func-8.sentence-5)
|
||||
|
||||
[*Note [2](#func-note-2)*:
|
||||
|
||||
If rcu_retire exits via an exception, no evaluation
|
||||
is scheduled[.](#func-8.sentence-6)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[9](#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[.](#func-9.sentence-1)
|
||||
|
||||
[10](#func-10)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13260)
|
||||
|
||||
[*Note [3](#func-note-3)*:
|
||||
|
||||
If scheduled evaluations acquire resources
|
||||
held across any invocation of rcu_retire on dom,
|
||||
deadlock can occur[.](#func-10.sentence-1)
|
||||
|
||||
â *end note*]
|
||||
131
cppdraft/saferecl/rcu/domain/func.md
Normal file
131
cppdraft/saferecl/rcu/domain/func.md
Normal file
@@ -0,0 +1,131 @@
|
||||
[saferecl.rcu.domain.func]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#rcu.domain.func)
|
||||
|
||||
### 32.11.2 Read-copy update (RCU) [[saferecl.rcu]](saferecl.rcu#domain.func)
|
||||
|
||||
#### 32.11.2.4 Class rcu_domain [[saferecl.rcu.domain]](saferecl.rcu.domain#func)
|
||||
|
||||
#### 32.11.2.4.3 Non-member functions [saferecl.rcu.domain.func]
|
||||
|
||||
[ð](#lib:rcu_default_domain)
|
||||
|
||||
`rcu_domain& rcu_default_domain() noexcept;
|
||||
`
|
||||
|
||||
[1](#1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13177)
|
||||
|
||||
*Returns*: A reference to a static-duration object of type rcu_domain[.](#1.sentence-1)
|
||||
|
||||
A reference to the same object is returned every time this function is called[.](#1.sentence-2)
|
||||
|
||||
[ð](#lib:rcu_synchronize)
|
||||
|
||||
`void rcu_synchronize(rcu_domain& dom = rcu_default_domain()) noexcept;
|
||||
`
|
||||
|
||||
[2](#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[.](#2.sentence-1)
|
||||
|
||||
[3](#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[.](#3.sentence-1)
|
||||
|
||||
[ð](#lib:rcu_barrier)
|
||||
|
||||
`void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept;
|
||||
`
|
||||
|
||||
[4](#4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13207)
|
||||
|
||||
*Effects*: May evaluate any scheduled evaluations in dom[.](#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[.](#4.sentence-2)
|
||||
|
||||
[5](#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[.](#5.sentence-1)
|
||||
|
||||
[*Note [1](#note-1)*:
|
||||
|
||||
A call to rcu_barrier does not imply
|
||||
a call to rcu_synchronize and vice versa[.](#5.sentence-2)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[ð](#lib:rcu_retire)
|
||||
|
||||
`template<class T, class D = default_delete<T>>
|
||||
void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());
|
||||
`
|
||||
|
||||
[6](#6)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13232)
|
||||
|
||||
*Mandates*: is_move_constructible_v<D> is true and
|
||||
the expression d(p) is well-formed[.](#6.sentence-1)
|
||||
|
||||
[7](#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[.](#7.sentence-1)
|
||||
|
||||
[8](#8)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13242)
|
||||
|
||||
*Effects*: May allocate memory[.](#8.sentence-1)
|
||||
|
||||
It is unspecified whether the memory allocation
|
||||
is performed by invoking operator new[.](#8.sentence-2)
|
||||
|
||||
Initializes an object d1 of type D from std::move(d)[.](#8.sentence-3)
|
||||
|
||||
Schedules the evaluation of d1(p) in the domain dom;
|
||||
the behavior is undefined if that evaluation exits via an exception[.](#8.sentence-4)
|
||||
|
||||
May invoke scheduled evaluations in dom[.](#8.sentence-5)
|
||||
|
||||
[*Note [2](#note-2)*:
|
||||
|
||||
If rcu_retire exits via an exception, no evaluation
|
||||
is scheduled[.](#8.sentence-6)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[9](#9)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13256)
|
||||
|
||||
*Throws*: bad_alloc or any exception thrown by the initialization of d1[.](#9.sentence-1)
|
||||
|
||||
[10](#10)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13260)
|
||||
|
||||
[*Note [3](#note-3)*:
|
||||
|
||||
If scheduled evaluations acquire resources
|
||||
held across any invocation of rcu_retire on dom,
|
||||
deadlock can occur[.](#10.sentence-1)
|
||||
|
||||
â *end note*]
|
||||
30
cppdraft/saferecl/rcu/domain/general.md
Normal file
30
cppdraft/saferecl/rcu/domain/general.md
Normal file
@@ -0,0 +1,30 @@
|
||||
[saferecl.rcu.domain.general]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#rcu.domain.general)
|
||||
|
||||
### 32.11.2 Read-copy update (RCU) [[saferecl.rcu]](saferecl.rcu#domain.general)
|
||||
|
||||
#### 32.11.2.4 Class rcu_domain [[saferecl.rcu.domain]](saferecl.rcu.domain#general)
|
||||
|
||||
#### 32.11.2.4.1 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](#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[.](#1.sentence-1)
|
||||
|
||||
[*Example [1](#example-1)*: std::scoped_lock<rcu_domain> rlock(rcu_default_domain()); â *end example*]
|
||||
|
||||
[2](#2)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13099)
|
||||
|
||||
The functions lock and unlock establish
|
||||
(possibly nested) regions of RCU protection[.](#2.sentence-1)
|
||||
90
cppdraft/saferecl/rcu/domain/members.md
Normal file
90
cppdraft/saferecl/rcu/domain/members.md
Normal file
@@ -0,0 +1,90 @@
|
||||
[saferecl.rcu.domain.members]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#rcu.domain.members)
|
||||
|
||||
### 32.11.2 Read-copy update (RCU) [[saferecl.rcu]](saferecl.rcu#domain.members)
|
||||
|
||||
#### 32.11.2.4 Class rcu_domain [[saferecl.rcu.domain]](saferecl.rcu.domain#members)
|
||||
|
||||
#### 32.11.2.4.2 Member functions [saferecl.rcu.domain.members]
|
||||
|
||||
[ð](#lib:lock,rcu_domain)
|
||||
|
||||
`void lock() noexcept;
|
||||
`
|
||||
|
||||
[1](#1)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13111)
|
||||
|
||||
*Effects*: Opens a region of RCU protection[.](#1.sentence-1)
|
||||
|
||||
[2](#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[.](#2.sentence-1)
|
||||
|
||||
[ð](#lib:try_lock,rcu_domain)
|
||||
|
||||
`bool try_lock() noexcept;
|
||||
`
|
||||
|
||||
[3](#3)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13127)
|
||||
|
||||
*Effects*: Equivalent to lock()[.](#3.sentence-1)
|
||||
|
||||
[4](#4)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13131)
|
||||
|
||||
*Returns*: true[.](#4.sentence-1)
|
||||
|
||||
[ð](#lib:unlock,rcu_domain)
|
||||
|
||||
`void unlock() noexcept;
|
||||
`
|
||||
|
||||
[5](#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[.](#5.sentence-1)
|
||||
|
||||
[6](#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[.](#6.sentence-1)
|
||||
|
||||
May invoke scheduled evaluations in *this[.](#6.sentence-2)
|
||||
|
||||
[7](#7)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13154)
|
||||
|
||||
[*Note [1](#note-1)*:
|
||||
|
||||
If such evaluations acquire resources
|
||||
held across any invocation of unlock on *this,
|
||||
deadlock can occur[.](#7.sentence-1)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[8](#8)
|
||||
|
||||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/threads.tex#L13161)
|
||||
|
||||
*Remarks*: Calls to unlock do not introduce a data race involving *this[.](#8.sentence-1)
|
||||
|
||||
[*Note [2](#note-2)*:
|
||||
|
||||
Evaluation of scheduled evaluations can still cause a data race[.](#8.sentence-2)
|
||||
|
||||
â *end note*]
|
||||
75
cppdraft/saferecl/rcu/general.md
Normal file
75
cppdraft/saferecl/rcu/general.md
Normal file
@@ -0,0 +1,75 @@
|
||||
[saferecl.rcu.general]
|
||||
|
||||
# 32 Concurrency support library [[thread]](./#thread)
|
||||
|
||||
## 32.11 Safe reclamation [[saferecl]](saferecl#rcu.general)
|
||||
|
||||
### 32.11.2 Read-copy update (RCU) [[saferecl.rcu]](saferecl.rcu#general)
|
||||
|
||||
#### 32.11.2.1 General [saferecl.rcu.general]
|
||||
|
||||
[1](#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[.](#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[.](#1.sentence-2)
|
||||
|
||||
[2](#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<T, D> for some D, and that base is public and non-virtual, and
|
||||
it has no base classes of type rcu_obj_base<X, Y> for any other combination X, Y[.](#2.sentence-1)
|
||||
|
||||
An object is rcu-protectable if it is of rcu-protectable type[.](#2.sentence-2)
|
||||
|
||||
[3](#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)](#3.1)
|
||||
|
||||
no other invocation of lock on dom is sequenced after L and before U, or
|
||||
|
||||
- [(3.2)](#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[.](#3.sentence-1)
|
||||
|
||||
[*Note [1](#note-1)*:
|
||||
|
||||
This pairs nested locks and unlocks on a given domain in each thread[.](#3.sentence-2)
|
||||
|
||||
â *end note*]
|
||||
|
||||
[4](#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[.](#4.sentence-1)
|
||||
|
||||
[5](#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[.](#5.sentence-1)
|
||||
|
||||
[6](#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[.](#6.sentence-1)
|
||||
|
||||
Each scheduled evaluation is evaluated at most once[.](#6.sentence-2)
|
||||
Reference in New Issue
Block a user