MySQL 8.3.0
Source Code Documentation
my_rcu_lock.h
Go to the documentation of this file.
1/* Copyright (c) 2018, 2023, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23#ifndef MY_RCU_LOCK_INCLUDED
24#define MY_RCU_LOCK_INCLUDED
25
26#include <atomic>
27#include <functional>
28
29#include "my_compiler.h"
30#include "my_systime.h"
31
32/**
33 A class that implements a limited version of the Read-Copy-Update lock pattern
34
35 When you have a global variable that is mostly read and rarely changed you
36 need to make sure your readers get minimal overhead eventually at the cost
37 of slowing down your writers.
38
39 It is implemented as an atomic pointer to the protected global (the template
40 class). You can safely read from it and you can safely write a new version of
41 the global. The problem is how and when to dispose of the old version(s) of
42 the global that are swapped out by the write.
43
44 The full RCU implementation solves this in a very generic way.
45 But we here take a simplification: we assume that there will be frequent times
46 when there's not gonna be active readers.
47
48 If we count atomically the active readers (by calling @ref
49 MyRcuLock::rcu_read() before reading and by calling @ref
50 MyRcuLock::rcu_end_read() when we're done using the value of the global we
51 have received from @ref MyRcuLock::rcu_read() we can detect these times by
52 simply looping over the atomic count (@ref MyRcuLock::rcu_readers_) and
53 waiting for a 0 to come out (this is exactly what
54 @ref MyRcuLock::wait_for_no_readers() does). Once we get a zero we know
55 that no existing readers will use the old value(s) of the global and new
56 readers will get the new value of the global. So if we get that zero we know
57 that we can safely dispose of all the old values we hold.
58
59 Intended usage
60
61 We instantiate @ref MyRcuLock once for each global we want to protect.
62
63 To read we instantiate the utility class @ref MyRcuLock::ReadLock and
64 we access the value by casting it to the type we need. Once the @ref
65 MyRcuLock::ReadLock goes out of scope we can't access the global value
66 or copies of it anymore.
67
68 To write we fully prepare the new value of the global and then call
69 @ref MyRcuLock::rcu_write(). That returns the old value of the global.
70
71 Now we need to dispose of that old value. For that we wait until there's no
72 active readers by calling @ref MyRcuLock::wait_for_no_readers() and
73 then, if that succeeds we delete the old value. This is exactly what @ref
74 MyRcuLock::write_wait_and_delete() does.
75
76 @tparam T The class of the global protected pointer
77*/
78template <typename T>
79class MyRcuLock {
80 public:
81 /**
82 Set up the RCU lock and the global.
83
84 Initializes the global and the reader counter.
85
86 @param init A value to store into the global. Can be NULL. Should not be
87 used after the call
88 */
89 MyRcuLock(const T *init) {
90 rcu_global_.store(init);
91 rcu_readers_.store(0);
92 }
93
94 /** disabled */
95 MyRcuLock(const MyRcuLock &) = delete;
96 /** disabled */
97 void operator=(const MyRcuLock &) = delete;
98
99 /**
100 Destructor.
101 Calls @ref MyRcuLock::write_wait_and_delete to set the global
102 to NULL and dispose of the old value
103 */
105
106 /**
107 High level read API for readers
108
109 A convenience scope guard class for readers.Use this for all of the RCU
110 global readers.
111 */
112 class ReadLock {
113 public:
114 /** get the value through the scope guard */
115 operator const T *() { return _lock->rcu_global_; }
116 /** construct a new read lock scope guard */
118 ReadLock(const ReadLock &) = delete;
120
121 protected:
122 MyRcuLock operator=(const ReadLock) = delete;
124 };
125
126 /**
127 Low level API: start reading
128
129 Returns a copy of the global that you can safely use until @ref
130 MyRcuLock::rcu_end_read() is called
131 @warning Don't forget to call @ref MyRcuLock::rcu_end_read() when done.
132 @return the copy of the global
133 */
134 const T *rcu_read() {
135 rcu_readers_.fetch_add(1, std::memory_order_relaxed);
136 return rcu_global_.load(std::memory_order_relaxed);
137 }
138
139 /**
140 Low level API: end reading
141
142 Marks the place where the pointer returned by @ref MyRcuLock::read() is
143 not going to be accessed any longer.
144
145 The recommended high level API is @ref MyRcuLock::ReadLock
146 @warning Each call to @ref MyRcuLock::rcu_read() must be coupled with a
147 call to
148 @ref MyRcuLock::rcu_end_read(). And the value returned by @ref
149 MyRcuLock::read() should not be used after calling @ref
150 MyRcuLock::rcu_end_read()
151 */
152 void rcu_end_read() { rcu_readers_.fetch_sub(1, std::memory_order_relaxed); }
153
154 /**
155 Low level API: write a new global and return the old one
156
157 The high level API for writers is @ref MyRcuLock::write_wait_and_delete()
158
159 @note you need to safely dispose of the returned old global.
160 @param newT a pointer to a fully prepared new global that starts getting
161 used immediately after being set.
162 @return the old value of the global
163 */
166 /**
167 @sa @ref MyRcuLock::wait_for_no_readers()
168 */
170
171 const T *rcu_write(const T *newT) {
172 return rcu_global_.exchange(newT, std::memory_order_release);
173 }
174
175 /**
176 Low level API: wait for no active readers
177
178 Call this to wait for a state when there's no active readers.
179 Optionally pass a functor to call at each check and stop if instructed.
180
181 The high level API to call is @ref MyRcuLock::write_wait_and_delete()
182
183 @retval true if the function was stopped by the functor
184 @retval false if an actual moment of 0 readers was detected
185 */
187 bool stopped = false;
188 while (rcu_readers_.load(std::memory_order_relaxed) > 0) my_sleep(10000);
189 return stopped;
190 }
191
192 /**
193 A RCU writer API
194
195 Stores a new value into the global, waits for when it's safe to dispose of
196 the old one and calls delete on it.
197
198 @param newT The new value to store into the global
199
200 @retval true the function was stopped by the functor. The old value was
201 lost.
202 @retval false operation succeeded and the old value was disposed of
203 */
204 bool write_wait_and_delete(const T *newT) {
205 const T *oldT = this->rcu_write(newT);
206 if (!oldT) return false;
207 if (!wait_for_no_readers()) {
208 delete oldT;
209 return false;
210 }
211 // we leak the oldT here
212 return true;
213 }
214
215 protected:
216 /** the global pointer to protect */
217 std::atomic<const T *> rcu_global_;
218 /** padding to break the CPU cache lines */
219 char rcu_padding_[128];
220 /** the readers counter */
221 std::atomic<long> rcu_readers_;
222};
223#endif /* ifndef MY_RCU_LOCK_INCLUDED */
static mysql_service_status_t init()
Component initialization.
Definition: audit_api_message_emit.cc:570
High level read API for readers.
Definition: my_rcu_lock.h:112
ReadLock(MyRcuLock *l)
construct a new read lock scope guard
Definition: my_rcu_lock.h:117
~ReadLock()
Definition: my_rcu_lock.h:119
MyRcuLock operator=(const ReadLock)=delete
MyRcuLock * _lock
Definition: my_rcu_lock.h:123
ReadLock(const ReadLock &)=delete
A class that implements a limited version of the Read-Copy-Update lock pattern.
Definition: my_rcu_lock.h:79
bool wait_for_no_readers()
Low level API: wait for no active readers.
Definition: my_rcu_lock.h:186
MyRcuLock(const T *init)
Set up the RCU lock and the global.
Definition: my_rcu_lock.h:89
char rcu_padding_[128]
padding to break the CPU cache lines
Definition: my_rcu_lock.h:219
void operator=(const MyRcuLock &)=delete
disabled
bool write_wait_and_delete(const T *newT)
A RCU writer API.
Definition: my_rcu_lock.h:204
const T * rcu_write(const T *newT)
Low level API: write a new global and return the old one.
Definition: my_rcu_lock.h:171
MyRcuLock(const MyRcuLock &)=delete
disabled
void rcu_end_read()
Low level API: end reading.
Definition: my_rcu_lock.h:152
std::atomic< const T * > rcu_global_
the global pointer to protect
Definition: my_rcu_lock.h:217
std::atomic< long > rcu_readers_
the readers counter
Definition: my_rcu_lock.h:221
~MyRcuLock()
Destructor.
Definition: my_rcu_lock.h:104
const T * rcu_read()
Low level API: start reading.
Definition: my_rcu_lock.h:134
Header for compiler-dependent features.
#define MY_COMPILER_DIAGNOSTIC_PUSH()
save the compiler's diagnostic (enabled warnings, errors, ...) state
Definition: my_compiler.h:284
#define MY_COMPILER_DIAGNOSTIC_POP()
restore the compiler's diagnostic (enabled warnings, errors, ...) state
Definition: my_compiler.h:285
#define MY_COMPILER_CLANG_WORKAROUND_REF_DOCBUG()
ignore -Wdocumentation compiler warnings for @see @ref
Definition: my_compiler.h:331
Defines for getting and processing the current system type programmatically.
void my_sleep(time_t m_seconds)
Wait a given number of microseconds.
Definition: my_systime.h:63