// bslmt_readerwritermutex.h                                          -*-C++-*-

#ifndef INCLUDED_BSLMT_READERWRITERMUTEX
#define INCLUDED_BSLMT_READERWRITERMUTEX

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a multi-reader/single-writer lock.
//
//@CLASSES:
//   bslmt::ReaderWriterMutex: multi-reader/single-writer lock class
//
//@SEE_ALSO: bslmt_readerwriterlock, bslmt_readlockguard,
//           bslmt_writelockguard, 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
///- - - - - - - - - - - - - - - - - - -
//: o '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.
//:
//: o '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.
//:
//: o '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());
//..

#include <bslscm_version.h>

#include <bslmt_mutex.h>
#include <bslmt_readerwritermuteximpl.h>
#include <bslmt_semaphore.h>

#include <bsls_atomicoperations.h>

namespace BloombergLP {
namespace bslmt {

                         // =======================
                         // class ReaderWriterMutex
                         // =======================

class ReaderWriterMutex {
    // This class provides a multi-reader/single-writer lock mechanism.

    // DATA
    ReaderWriterMutexImpl<bsls::AtomicOperations, Mutex, Semaphore> d_impl;

  private:
    // NOT IMPLEMENTED
    ReaderWriterMutex(const ReaderWriterMutex&);
    ReaderWriterMutex& operator=(const ReaderWriterMutex&);

  public:
    // CREATORS
    ReaderWriterMutex();
        // Construct a reader/writer lock initialized to an unlocked state.

    //! ~ReaderWriterMutex();
        // Destroy this object

    // MANIPULATORS
    void lockRead();
        // Lock this reader-writer mutex for reading.  If there are no active
        // or pending write locks, lock this mutex for reading and return
        // immediately.  Otherwise, block until the read lock on this mutex is
        // acquired.  Use 'unlockRead' or 'unlock' to release the lock on this
        // mutex.  The behavior is undefined if this method is called from a
        // thread that already has a lock on this mutex.

    void lockWrite();
        // Lock this reader-writer mutex for writing.  If there are no active
        // or pending locks on this mutex, lock this mutex for writing and
        // return immediately.  Otherwise, block until the write lock on this
        // mutex is acquired.  Use 'unlockWrite' or 'unlock' to release the
        // lock on this mutex.  The behavior is undefined if this method is
        // called from a thread that already has a lock on this mutex.

    int tryLockRead();
        // Attempt to lock this reader-writer mutex for reading.  Immediately
        // return 0 on success, and a non-zero value if there are active or
        // pending writers.  If successful, 'unlockRead' or 'unlock' must be
        // used to release the lock on this mutex.  The behavior is undefined
        // if this method is called from a thread that already has a lock on
        // this mutex.

    int tryLockWrite();
        // Attempt to lock this reader-writer mutex for writing.  Immediately
        // return 0 on success, and a non-zero value if there are active or
        // pending locks on this mutex.  If successful, 'unlockWrite' or
        // 'unlock' must be used to release the lock on this mutex.  The
        // behavior is undefined if this method is called from a thread that
        // already has a lock on this mutex.

    void unlock();
        // Release the lock that the calling thread holds on this reader-writer
        // mutex.  The behavior is undefined unless the calling thread
        // currently has a lock on this mutex.

    void unlockRead();
        // Release the read lock that the calling thread holds on this
        // reader-writer mutex.  The behavior is undefined unless the calling
        // thread currently has a read lock on this mutex.

    void unlockWrite();
        // Release the write lock that the calling thread holds on this
        // reader-writer mutex.  The behavior is undefined unless the calling
        // thread currently has a write lock on this mutex.

    // ACCESSORS
    bool isLocked() const;
        // Return 'true' if this reader-write mutex is currently read locked or
        // write locked, and 'false' otherwise.

    bool isLockedRead() const;
        // Return 'true' if this reader-write mutex is currently read locked,
        // and 'false' otherwise.

    bool isLockedWrite() const;
        // Return 'true' if this reader-write mutex is currently write locked,
        // and 'false' otherwise.
};

}  // close package namespace

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

                         // -----------------------
                         // class ReaderWriterMutex
                         // -----------------------

// CREATORS
inline
bslmt::ReaderWriterMutex::ReaderWriterMutex()
{
}

// MANIPULATORS
inline
void bslmt::ReaderWriterMutex::lockRead()
{
    d_impl.lockRead();
}

inline
void bslmt::ReaderWriterMutex::lockWrite()
{
    d_impl.lockWrite();
}

inline
int bslmt::ReaderWriterMutex::tryLockRead()
{
    return d_impl.tryLockRead();
}

inline
int bslmt::ReaderWriterMutex::tryLockWrite()
{
    return d_impl.tryLockWrite();
}

inline
void bslmt::ReaderWriterMutex::unlock()
{
    d_impl.unlock();
}

inline
void bslmt::ReaderWriterMutex::unlockRead()
{
    d_impl.unlockRead();
}

inline
void bslmt::ReaderWriterMutex::unlockWrite()
{
    d_impl.unlockWrite();
}

// ACCESSORS
inline
bool bslmt::ReaderWriterMutex::isLocked() const
{
    return d_impl.isLocked();
}

inline
bool bslmt::ReaderWriterMutex::isLockedRead() const
{
    return d_impl.isLockedRead();
}

inline
bool bslmt::ReaderWriterMutex::isLockedWrite() const
{
    return d_impl.isLockedWrite();
}

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2016 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------- END-OF-FILE ----------------------------------