// 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
///--------------------------------------
// * `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:
// ```
// // This `class` represents a bank account with a single balance.
// class my_Account {
//
//     // 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
//
//     /// Create an account with zero balance.
//     my_Account();
//
//     /// Create an account having the value of the specified `original`
//     /// account.
//     my_Account(const my_Account& original);
//
//     /// Destroy this account.
//     ~my_Account();
//
//     // MANIPULATORS
//
//     /// 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.
//     my_Account& operator=(const my_Account& rhs);
//
//     /// Atomically deposit the specified `pennies` into this account.
//     /// Note that this operation is thread-safe; no `lock` is needed.
//     void deposit(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.
//     int withdraw(bsls::Types::Uint64 pennies);
//
//     // ACCESSORS
//
//     /// Atomically return the number of pennies that are available for
//     /// withdrawal from this account.
//     bsls::Types::Uint64 balanceInPennies() const;
// };
//
// // 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
                         // =======================

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

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

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

  public:
    // CREATORS

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

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

    // MANIPULATORS

    /// 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 lockRead();

    /// 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.
    void lockWrite();

    /// 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 tryLockRead();

    /// 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.
    int tryLockWrite();

    /// 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 unlock();

    /// 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 unlockRead();

    /// 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.
    void unlockWrite();

    // ACCESSORS

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

}  // close package namespace
}  // 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 ----------------------------------
