MySQL  8.0.21
Source Code Documentation
Go to the documentation of this file.
1 /* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
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.
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.
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  GNU General Public License, version 2.0, for more details.
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 */
26 #include <atomic>
27 #include <functional>
29 #include "my_compiler.h"
31 /**
32  A class that implements a limited version of the Read-Copy-Update lock pattern
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.
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.
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.
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.
58  Intended usage
60  We instantiate @ref MyRcuLock once for each global we want to protect.
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.
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.
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.
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.
83  Initializes the global and the reader counter.
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) {
91  }
93  /** disabled */
94  MyRcuLock(const MyRcuLock &) = delete;
95  /** disabled */
96  void operator=(const MyRcuLock &) = delete;
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  */
105  /**
106  High level read API for readers
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;
120  protected:
121  MyRcuLock operator=(const ReadLock) = delete;
123  };
125  /**
126  Low level API: start reading
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  }
138  /**
139  Low level API: end reading
141  Marks the place where the pointer returned by @ref MyRcuLock::read() is
142  not going to be accessed any longer.
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); }
153  /**
154  Low level API: write a new global and return the old one
156  The high level API for writers is @ref MyRcuLock::write_wait_and_delete()
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  */
170  const T *rcu_write(const T *newT) {
171  return, std::memory_order_release);
172  }
174  /**
175  Low level API: wait for no active readers
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.
180  The high level API to call is @ref MyRcuLock::write_wait_and_delete()
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  }
192  /**
193  A RCU writer API
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.
198  @param newT The new value to store into the global
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  }
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 */
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
Definition: my_rcu_lock.h:118
static mysql_service_status_t init()
Component initialization.
ReadLock(MyRcuLock *l)
construct a new read lock scope guard
Definition: my_rcu_lock.h:116
MyRcuLock operator=(const ReadLock)=delete
Definition: my_rcu_lock.h:103
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
ignore -Wdocumentation compiler warnings for @see @ref
Definition: my_compiler.h:342
std::future< void > stopped
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