[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*]
