Quick Links:

bal | bbl | bdl | bsl

Namespaces

Component bslmt_readerwritermutex
[Package bslmt]

Provide a multi-reader/single-writer lock. More...

Namespaces

namespace  bslmt

Detailed Description

Outline
Purpose:
Provide a multi-reader/single-writer lock.
Classes:
bslmt::ReaderWriterMutex multi-reader/single-writer lock class
See also:
Component bslmt_readerwriterlock, Component bslmt_readlockguard, Component bslmt_writelockguard, Component bslmt_readerwriterlockassert
Description:
This component defines an efficient multi-reader/single-writer lock mechanism, bslmt::ReaderWriterMutex. It is designed to allow concurrent read access to a shared resource while still controlling write access.
Reader-writer locks are generally used for resources that are frequently read and less frequently updated. Unlike other lock mechanisms (e.g., "mutexes"), reader-writer locks provide two distinct but mutually exclusive lock states: a read lock state, and a write lock state.
To the extent the implementation's underlying mutex prevents a thread from starving, readers can not be starved by writers and writers can not be starved by readers. If the underlying mutex, to some extent, favors re-acquisition of the mutex to allowing a new thread to obtain the mutex (e.g., the mutex obtained on Linux), this reader-writer lock is writer biased since writers can re-acquire the lock in the presence of readers but readers will not be able to re-acquire the lock in the presence of writers.
bslmt Read/Write Locking Components:
  • bslmt::ReaderWriterMutex (defined in this component). Preferred for most use-cases, has been shown to be faster than bslmt::ReaderWriterLock under most conditions and is generally the best choice.
  • bslmt::ReaderWriterLock: Preferred only when very long hold times are anticipated. It also provides upgrade* methods from a locked-for-read state to a locked-for-write state, but the use of this feature is discouraged as it has performed poorly on benchmarks.
  • bslmt::RWMutex: Deprecated.
Note that for extremely short hold times and very high concurrency, a bslmt::Mutex might outperform all of the above.
Usage:
This section illustrates intended use of this component.
Example 1: Maintaining an Account Balance:
The following snippets of code illustrate the use of bslmt::ReaderWriterMutex to write a thread-safe class, my_Account. Note the typical use of mutable for the lock:
  class my_Account {
      // This 'class' represents a bank account with a single balance.

      // DATA
      bsls::Types::Uint64               d_pennies;  // amount of money in the
                                                    // account

      mutable bslmt::ReaderWriterMutex  d_lock;     // guard access to
                                                    // 'd_account_p'

    public:
      // CREATORS
      my_Account();
          // Create an account with zero balance.

      my_Account(const my_Account& original);
          // Create an account having the value of the specified 'original'
          // account.

      ~my_Account();
          // Destroy this account.

      // MANIPULATORS
      my_Account& operator=(const my_Account& rhs);
          // Atomically assign to this account the value of the specified
          // 'rhs' account, and return a reference to this modifiable
          // account.  Note that this operation is thread-safe; no 'lock' is
          // needed.

      void deposit(bsls::Types::Uint64 pennies);
          // Atomically deposit the specified 'pennies' into this account.
          // Note that this operation is thread-safe; no 'lock' is needed.

      int withdraw(bsls::Types::Uint64 pennies);
          // Attempt to atomically withdraw the specified 'pennies' from this
          // account.  Return 0 on success and update this account to reflect
          // the withdrawal.  Otherwise, return a non-zero value and do not
          // update the balance of this account.  Note that this operation is
          // thread-safe; no 'lock' is needed.

      // ACCESSORS
      bsls::Types::Uint64 balanceInPennies() const;
          // Atomically return the number of pennies that are available for
          // withdrawal from this account.
  };

  // CREATORS
  my_Account::my_Account()
  : d_pennies(0)
  {
  }

  my_Account::my_Account(const my_Account& original)
  : d_pennies(original.balanceInPennies())
  {
  }

  my_Account::~my_Account()
  {
  }

  // MANIPULATORS
  my_Account& my_Account::operator=(const my_Account& rhs)
  {
Where appropriate, clients should use a lock-guard to ensure that an acquired mutex is always properly released, even if an exception is thrown.
      d_lock.lockWrite();
      d_pennies = rhs.balanceInPennies();
      d_lock.unlockWrite();
      return *this;
  }

  void my_Account::deposit(bsls::Types::Uint64 pennies)
  {
      d_lock.lockWrite();
      d_pennies += pennies;
      d_lock.unlockWrite();
  }

  int my_Account::withdraw(bsls::Types::Uint64 pennies)
  {
      int rv = 0;

      d_lock.lockWrite();

      if (pennies <= d_pennies) {
          d_pennies -= pennies;
      }
      else {
          rv = 1;
      }

      d_lock.unlockWrite();

      return rv;
  }

  // ACCESSORS
  bsls::Types::Uint64 my_Account::balanceInPennies() const
  {
      d_lock.lockRead();
      bsls::Types::Uint64 rv = d_pennies;
      d_lock.unlockRead();
      return rv;
  }
The atomic my_Account methods are used as expected:
  my_Account account;

  account.deposit(10050);
  assert(10050 == account.balanceInPennies());

  bsls::Types::Uint64 paycheckInPennies = 5025;

  account.deposit(paycheckInPennies);
  assert(15075 == account.balanceInPennies());