MySQL 8.0.39
Source Code Documentation
MyRcuLock< T > Class Template Reference

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...
 

Detailed Description

template<typename T>
class MyRcuLock< T >

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.

Template Parameters
TThe class of the global protected pointer

Constructor & Destructor Documentation

◆ MyRcuLock() [1/2]

template<typename T >
MyRcuLock< T >::MyRcuLock ( const T *  init)
inline

Set up the RCU lock and the global.

Initializes the global and the reader counter.

Parameters
initA value to store into the global. Can be NULL. Should not be used after the call

◆ MyRcuLock() [2/2]

template<typename T >
MyRcuLock< T >::MyRcuLock ( const MyRcuLock< T > &  )
delete

disabled

◆ ~MyRcuLock()

template<typename T >
MyRcuLock< T >::~MyRcuLock ( )
inline

Destructor.

Calls MyRcuLock::write_wait_and_delete to set the global to NULL and dispose of the old value

Member Function Documentation

◆ operator=()

template<typename T >
void MyRcuLock< T >::operator= ( const MyRcuLock< T > &  )
delete

disabled

◆ rcu_end_read()

template<typename T >
void MyRcuLock< T >::rcu_end_read ( )
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

Warning
Each call to MyRcuLock::rcu_read() must be coupled with a call to MyRcuLock::rcu_end_read(). And the value returned by MyRcuLock::read() should not be used after calling MyRcuLock::rcu_end_read()

◆ rcu_read()

template<typename T >
const T * MyRcuLock< T >::rcu_read ( )
inline

Low level API: start reading.

Returns a copy of the global that you can safely use until MyRcuLock::rcu_end_read() is called

Warning
Don't forget to call MyRcuLock::rcu_end_read() when done.
Returns
the copy of the global

◆ rcu_write()

template<typename T >
const T * MyRcuLock< T >::rcu_write ( const T *  newT)
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()

Note
you need to safely dispose of the returned old global.
Parameters
newTa pointer to a fully prepared new global that starts getting used immediately after being set.
Returns
the old value of the global
See also
MyRcuLock::wait_for_no_readers()

◆ wait_for_no_readers()

template<typename T >
bool MyRcuLock< T >::wait_for_no_readers ( )
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()

Return values
trueif the function was stopped by the functor
falseif an actual moment of 0 readers was detected

◆ write_wait_and_delete()

template<typename T >
bool MyRcuLock< T >::write_wait_and_delete ( const T *  newT)
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.

Parameters
newTThe new value to store into the global
Return values
truethe function was stopped by the functor. The old value was lost.
falseoperation succeeded and the old value was disposed of

Member Data Documentation

◆ rcu_global_

template<typename T >
std::atomic<const T *> MyRcuLock< T >::rcu_global_
protected

the global pointer to protect

◆ rcu_padding_

template<typename T >
char MyRcuLock< T >::rcu_padding_[128]
protected

padding to break the CPU cache lines

◆ rcu_readers_

template<typename T >
std::atomic<long> MyRcuLock< T >::rcu_readers_
protected

the readers counter


The documentation for this class was generated from the following file: