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