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