rclcpp  master
C++ ROS Client Library API
Classes | Public Member Functions | Protected Attributes | List of all members
rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock Class Referencefinal

Writer-perferring read-write lock. More...

#include <write_preferring_read_write_lock.hpp>

Collaboration diagram for rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock:
Collaboration graph
[legend]

Classes

class  ReadMutex
 Read mutex for the WritePreferringReadWriteLock. More...
 
class  WriteMutex
 Write mutex for the WritePreferringReadWriteLock. More...
 

Public Member Functions

 WritePreferringReadWriteLock (std::function< void()> enter_waiting_function=nullptr)
 
ReadMutexget_read_mutex ()
 Return read mutex which can be used with standard constructs like std::lock_guard. More...
 
WriteMutexget_write_mutex ()
 Return write mutex which can be used with standard constructs like std::lock_guard. More...
 

Protected Attributes

bool reader_active_ = false
 
std::size_t number_of_writers_waiting_ = 0
 
bool writer_active_ = false
 
std::mutex mutex_
 
std::condition_variable condition_variable_
 
ReadMutex read_mutex_
 
WriteMutex write_mutex_
 
std::function< void()> enter_waiting_function_
 

Detailed Description

Writer-perferring read-write lock.

This class is based on an implementation of a "write-preferring RW lock" as described in this wikipedia page:

https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock#Using_a_condition_variable_and_a_mutex

Copying here for posterity:

*   For a write-preferring RW lock one can use two integer counters and one boolean flag:
*
*       num_readers_active: the number of readers that have acquired the lock (integer)
*       num_writers_waiting: the number of writers waiting for access (integer)
*       writer_active: whether a writer has acquired the lock (boolean).
*
*   Initially num_readers_active and num_writers_waiting are zero and writer_active is false.
*
*   The lock and release operations can be implemented as
*
*   Begin Read
*
*       Lock g
*       While num_writers_waiting > 0 or writer_active:
*           wait cond, g[a]
*       Increment num_readers_active
*       Unlock g.
*
*   End Read
*
*       Lock g
*       Decrement num_readers_active
*       If num_readers_active = 0:
*           Notify cond (broadcast)
*       Unlock g.
*
*   Begin Write
*
*       Lock g
*       Increment num_writers_waiting
*       While num_readers_active > 0 or writer_active is true:
*           wait cond, g
*       Decrement num_writers_waiting
*       Set writer_active to true
*       Unlock g.
*
*   End Write
*
*       Lock g
*       Set writer_active to false
*       Notify cond (broadcast)
*       Unlock g.
* 

It will prefer any waiting write calls to any waiting read calls, meaning that excessive write calls can starve read calls.

This class diverges from that design in two important ways. First, it is a single reader, single writer version. Second, it allows for user defined code to be run after a writer enters the waiting state, and the purpose of this feature is to allow the user to interrupt any potentially long blocking read activities.

Together these two features allow new waiting writers to not only ensure they get the lock before any queued readers, but also that it can safely interrupt read activities if needed, without allowing new read activities to start before it gains the lock.

The first difference prevents the case that a multiple read activities occur at the same time but the writer can only reliably interrupt one of them. By preventing multiple read activities concurrently, this case is avoided. The second difference allows the user to define how to interrupt read activity that could be blocking the write activities that need to happen as soon as possible.

To implement the differences, this class replaces the "num_readers_active" counter with a "reader_active" boolean. It also changes the "Begin Read" section from above, like this:

*   Begin Read
*
*       Lock g
*       While num_writers_waiting > 0 or writer_active or reader_active:  // changed
*           wait cond, g[a]
*       Set reader_active to true  // changed
*       Unlock g.
* 

And changes the "End Read" section from above, like this:

*   End Read
*
*       Lock g
*       Set reader_active to false  // changed
*       Notify cond (broadcast)  // changed, now unconditional
*       Unlock g.
* 

The "Begin Write" section is also updated as follows:

*   Begin Write
*
*       Lock g
*       Increment num_writers_waiting
*       Call user defined enter_waiting function  // new
*       While reader_active is true or writer_active is true:  // changed
*           wait cond, g
*       Decrement num_writers_waiting
*       Set writer_active to true
*       Unlock g.
* 

The implementation uses a single condition variable, single lock, and several state variables.

The typical use of this class is as follows:

class MyClass
{
  WritePreferringReadWriteLock wprw_lock_;
public:
  MyClass() {}
  void do_some_reading()
  {
    using rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock;
    std::lock_guard<WritePreferringReadWriteLock::ReadMutex> lock(wprw_lock_.get_read_mutex());
    // Do reading...
  }
  void do_some_writing()
  {
    using rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock;
    std::lock_guard<WritePreferringReadWriteLock::WriteMutex> lock(wprw_lock_.get_write_mutex());
    // Do writing...
  }
};

Constructor & Destructor Documentation

◆ WritePreferringReadWriteLock()

rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::WritePreferringReadWriteLock ( std::function< void()>  enter_waiting_function = nullptr)
explicit

Member Function Documentation

◆ get_read_mutex()

ReadMutex& rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::get_read_mutex ( )

Return read mutex which can be used with standard constructs like std::lock_guard.

◆ get_write_mutex()

WriteMutex& rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::get_write_mutex ( )

Return write mutex which can be used with standard constructs like std::lock_guard.

Member Data Documentation

◆ reader_active_

bool rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::reader_active_ = false
protected

◆ number_of_writers_waiting_

std::size_t rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::number_of_writers_waiting_ = 0
protected

◆ writer_active_

bool rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::writer_active_ = false
protected

◆ mutex_

std::mutex rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::mutex_
protected

◆ condition_variable_

std::condition_variable rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::condition_variable_
protected

◆ read_mutex_

ReadMutex rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::read_mutex_
protected

◆ write_mutex_

WriteMutex rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::write_mutex_
protected

◆ enter_waiting_function_

std::function<void()> rclcpp::wait_set_policies::detail::WritePreferringReadWriteLock::enter_waiting_function_
protected

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