MySQL 8.0.39
Source Code Documentation
|
A class that implements a limited version of the Read-Copy-Update lock pattern. More...
#include <my_rcu_lock.h>
Classes | |
class | ReadLock |
High level read API for readers. More... | |
Public Member Functions | |
MyRcuLock (const T *init) | |
Set up the RCU lock and the global. More... | |
MyRcuLock (const MyRcuLock &)=delete | |
disabled More... | |
void | operator= (const MyRcuLock &)=delete |
disabled More... | |
~MyRcuLock () | |
Destructor. More... | |
const T * | rcu_read () |
Low level API: start reading. More... | |
void | rcu_end_read () |
Low level API: end reading. More... | |
const T * | rcu_write (const T *newT) |
Low level API: write a new global and return the old one. More... | |
bool | wait_for_no_readers () |
Low level API: wait for no active readers. More... | |
bool | write_wait_and_delete (const T *newT) |
A RCU writer API. More... | |
Protected Attributes | |
std::atomic< const T * > | rcu_global_ |
the global pointer to protect More... | |
char | rcu_padding_ [128] |
padding to break the CPU cache lines More... | |
std::atomic< long > | rcu_readers_ |
the readers counter More... | |
A class that implements a limited version of the Read-Copy-Update lock pattern.
When you have a global variable that is mostly read and rarely changed you need to make sure your readers get minimal overhead eventually at the cost of slowing down your writers.
It is implemented as an atomic pointer to the protected global (the template class). You can safely read from it and you can safely write a new version of the global. The problem is how and when to dispose of the old version(s) of the global that are swapped out by the write.
The full RCU implementation solves this in a very generic way. But we here take a simplification: we assume that there will be frequent times when there's not gonna be active readers.
If we count atomically the active readers (by calling MyRcuLock::rcu_read() before reading and by calling MyRcuLock::rcu_end_read() when we're done using the value of the global we have received from MyRcuLock::rcu_read() we can detect these times by simply looping over the atomic count (MyRcuLock::rcu_readers_) and waiting for a 0 to come out (this is exactly what MyRcuLock::wait_for_no_readers() does). Once we get a zero we know that no existing readers will use the old value(s) of the global and new readers will get the new value of the global. So if we get that zero we know that we can safely dispose of all the old values we hold.
Intended usage
We instantiate MyRcuLock once for each global we want to protect.
To read we instantiate the utility class MyRcuLock::ReadLock and we access the value by casting it to the type we need. Once the MyRcuLock::ReadLock goes out of scope we can't access the global value or copies of it anymore.
To write we fully prepare the new value of the global and then call MyRcuLock::rcu_write(). That returns the old value of the global.
Now we need to dispose of that old value. For that we wait until there's no active readers by calling MyRcuLock::wait_for_no_readers() and then, if that succeeds we delete the old value. This is exactly what MyRcuLock::write_wait_and_delete() does.
T | The class of the global protected pointer |
Set up the RCU lock and the global.
Initializes the global and the reader counter.
init | A value to store into the global. Can be NULL. Should not be used after the call |
Destructor.
Calls MyRcuLock::write_wait_and_delete to set the global to NULL and dispose of the old value
|
inline |
Low level API: end reading.
Marks the place where the pointer returned by MyRcuLock::read() is not going to be accessed any longer.
The recommended high level API is MyRcuLock::ReadLock
|
inline |
Low level API: start reading.
Returns a copy of the global that you can safely use until MyRcuLock::rcu_end_read() is called
|
inline |
Low level API: write a new global and return the old one.
The high level API for writers is MyRcuLock::write_wait_and_delete()
newT | a pointer to a fully prepared new global that starts getting used immediately after being set. |
|
inline |
Low level API: wait for no active readers.
Call this to wait for a state when there's no active readers. Optionally pass a functor to call at each check and stop if instructed.
The high level API to call is MyRcuLock::write_wait_and_delete()
true | if the function was stopped by the functor |
false | if an actual moment of 0 readers was detected |
|
inline |
A RCU writer API.
Stores a new value into the global, waits for when it's safe to dispose of the old one and calls delete on it.
newT | The new value to store into the global |
true | the function was stopped by the functor. The old value was lost. |
false | operation succeeded and the old value was disposed of |
|
protected |
the global pointer to protect
|
protected |
padding to break the CPU cache lines
|
protected |
the readers counter