32 Concurrency support library [thread]

32.11 Safe reclamation [saferecl]

32.11.2 Read-copy update (RCU) [saferecl.rcu]

32.11.2.1 General [saferecl.rcu.general]

RCU is a synchronization mechanism that can be used for linked data structures that are frequently read, but seldom updated.
RCU does not provide mutual exclusion, but instead allows the user to schedule specified actions such as deletion at some later time.
A class type T is rcu-protectable 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.
An object is rcu-protectable if it is of rcu-protectable type.
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
  • no other invocation of lock on dom is sequenced after L and before U, or
  • 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.
[Note 1: 
This pairs nested locks and unlocks on a given domain in each thread.
— end note]
A region of RCU protection on a domain dom starts with a lock L on dom and ends with its corresponding unlock U.
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.
The evaluation of a scheduled evaluation is potentially concurrent with any other scheduled evaluation.
Each scheduled evaluation is evaluated at most once.

32.11.2.2 Header <rcu> synopsis [rcu.syn]

namespace std { // [saferecl.rcu.base], class template rcu_obj_base template<class T, class D = default_delete<T>> class rcu_obj_base; // [saferecl.rcu.domain], class rcu_domain class rcu_domain; // [saferecl.rcu.domain.func], 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 Class template rcu_obj_base [saferecl.rcu.base]

Objects of type T to be protected by RCU inherit from a specialization rcu_obj_base<T, D> for some D.
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 }; }
The behavior of a program that adds specializations for rcu_obj_base is undefined.
T may be an incomplete type.
It shall be complete before any member of the resulting specialization of rcu_obj_base is referenced.
D shall be a function object type ([function.objects]) for which, given a value d of type D and a value ptr of type T*, the expression d(ptr) is valid.
D shall meet the requirements for Cpp17DefaultConstructible and Cpp17MoveAssignable.
If D is trivially copyable, all specializations of rcu_obj_base<T, D> are trivially copyable.
void retire(D d = D(), rcu_domain& dom = rcu_default_domain()) noexcept;
Mandates: T is an rcu-protectable type.
Preconditions: *this is a base class subobject of an object x of type T.
The member function rcu_obj_base<T, D>​::​retire was not invoked on x before.
The assignment to deleter does not exit via an exception.
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.
May invoke scheduled evaluations in dom.
[Note 1: 
If such evaluations acquire resources held across any invocation of retire on dom, deadlock can occur.
— end note]

32.11.2.4 Class rcu_domain [saferecl.rcu.domain]

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; }; }
This class meets the requirements of Cpp17Lockable ([thread.req.lockable.req]) and provides regions of RCU protection.
[Example 1: std::scoped_lock<rcu_domain> rlock(rcu_default_domain()); — end example]
The functions lock and unlock establish (possibly nested) regions of RCU protection.

32.11.2.4.2 Member functions [saferecl.rcu.domain.members]

void lock() noexcept;
Effects: Opens a region of RCU protection.
Remarks: Calls to lock do not introduce a data race ([intro.races]) involving *this.
bool try_lock() noexcept;
Effects: Equivalent to lock().
Returns: true.
void unlock() noexcept;
Preconditions: A call to lock that opened an unclosed region of RCU protection is sequenced before the call to unlock.
Effects: Closes the unclosed region of RCU protection that was most recently opened.
May invoke scheduled evaluations in *this.
[Note 1: 
If such evaluations acquire resources held across any invocation of unlock on *this, deadlock can occur.
— end note]
Remarks: Calls to unlock do not introduce a data race involving *this.
[Note 2: 
Evaluation of scheduled evaluations can still cause a data race.
— end note]

32.11.2.4.3 Non-member functions [saferecl.rcu.domain.func]

rcu_domain& rcu_default_domain() noexcept;
Returns: A reference to a static-duration object of type rcu_domain.
A reference to the same object is returned every time this function is called.
void rcu_synchronize(rcu_domain& dom = rcu_default_domain()) noexcept;
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.
Synchronization: The unlock closing R strongly happens before the return from rcu_synchronize.
void rcu_barrier(rcu_domain& dom = rcu_default_domain()) noexcept;
Effects: May evaluate any scheduled evaluations in dom.
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.
Synchronization: The evaluation of any such E strongly happens before the return from rcu_barrier.
[Note 1: 
A call to rcu_barrier does not imply a call to rcu_synchronize and vice versa.
— end note]
template<class T, class D = default_delete<T>> void rcu_retire(T* p, D d = D(), rcu_domain& dom = rcu_default_domain());
Mandates: is_move_constructible_v<D> is true and the expression d(p) is well-formed.
Preconditions: D meets the Cpp17MoveConstructible and Cpp17Destructible requirements.
Effects: May allocate memory.
It is unspecified whether the memory allocation is performed by invoking operator new.
Initializes an object d1 of type D from std​::​move(d).
Schedules the evaluation of d1(p) in the domain dom; the behavior is undefined if that evaluation exits via an exception.
May invoke scheduled evaluations in dom.
[Note 2: 
If rcu_retire exits via an exception, no evaluation is scheduled.
— end note]
Throws: bad_alloc or any exception thrown by the initialization of d1.
[Note 3: 
If scheduled evaluations acquire resources held across any invocation of rcu_retire on dom, deadlock can occur.
— end note]