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