// bslmt_readlockguard.h                                              -*-C++-*-
#ifndef INCLUDED_BSLMT_READLOCKGUARD
#define INCLUDED_BSLMT_READLOCKGUARD

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

//@PURPOSE: Provide generic scoped guards for read synchronization objects.
//
//@CLASSES:
//  bslmt::ReadLockGuard: automatic locking-unlocking for read access
//  bslmt::ReadLockGuardUnlock: automatic unlocking-locking for read access
//  bslmt::ReadLockGuardTryLock: automatic non-blocking locking-unlocking
//  bslmt::LockReadGuard: DEPRECATED
//
//@SEE_ALSO: bslmt_lockguard, bslmt_writelockguard
//
//@DESCRIPTION: This component provides generic guards, `bslmt::ReadLockGuard`,
// `bslmt::ReadLockGuardUnlock`, and `bslmt::ReadLockGuardTryLock`, to
// automatically lock and unlock an external synchronization object for
// reading.  The synchronization object can be any type (e.g.,
// `bslmt::ReaderWriterLock`) that provides the following methods:
// ```
// void lockRead();
// void unlock();
// ```
// Both `bslmt::ReadLockGuard` and `bslmt::ReadLockGuardUnlock` implement the
// "construction is acquisition, destruction is release" idiom.  During
// construction, `bslmt::ReadLockGuard` automatically calls `lockRead` on the
// user-supplied object, and `unlock` when it is destroyed (unless released).
// `bslmt::ReadLockGuardUnlock` does the opposite -- it invokes the `unlock`
// method when constructed and the `lockRead` method when destroyed.
//
// A third type of guard, `bslmt::ReadLockGuardTryLock`, attempts to acquire a
// lock, and if acquisition succeeds, releases it upon destruction.  Since the
// acquisition is done at construction time, it is not possible to return a
// value to indicate success.  Rather, the `bslmt::ReadLockGuardTryLock`
// contains a pointer to the synchronization object if `tryLock` succeeds, and
// is null otherwise.  The synchronization object can be any type (e.g.,
// `bslmt::Mutex` or `bslmt::RecursiveMutex`) that provides the following
// methods:
// ```
// int tryLockRead();
// void unlock();
// ```
// Note that objects of none of these guard types assumes ownership of the
// synchronization object provided at construction.  Also note that objects of
// all of the guard types may be constructed with a null `lock` whereby the
// constructed guard objects guard no lock.  The destructor of each of the
// guard types has no effect if no lock is under management.
//
///Behavior of the `release` Method
///--------------------------------
// Like all BDE guard classes, each of the three `bslmt::ReadLockGuard*`
// classes provides a `release` method that terminates the guard's management
// of any lock object that the guard holds.  The `release` method has *no*
// *effect* on the state of the lock object.
//
// In particular, `bslmt::ReadLockGuard::release` does not unlock the lock
// object under management.  If a user wants to release the lock object *and*
// unlock the lock object (because the lock is no longer required before the
// guard goes out of scope), the following idiom can be used:
// ```
// // 'guard' is an existing guard of type 'bslmt::ReadLockGuard<my_RLock>',
// // created in a scope that we do not control.
//
// {
//     // ... Do work that requires the lock.
//
//     // We know that the lock is no longer needed.
//
//     my_RLock *rlock = guard.release();
//
//     // 'rlock' is no longer managed, but is *still* *locked*.
//
//     rlock->unlock();
//
//     // ... Do work that does not require the lock.
// }
// ```
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Usage
/// - - - - - - - - - - -
// Use this component to ensure that in the event of an exception or exit from
// any point in a given scope, the synchronization object will be properly
// unlocked.  The following function, `errorProneFunc`, is overly complex, not
// exception safe, and contains a bug.
// ```
// static void errorProneFunc(const my_Object *obj, my_RWLock *rwlock)
// {
//     rwlock->lockRead();
//     if (someCondition) {
//         obj->someMethod();
//         rwlock->unlock();
//         return;                                                   // RETURN
//     } else if (someOtherCondition) {
//         obj->someOtherMethod();
//         // MISTAKE! forgot to unlock rwlock
//         return;                                                   // RETURN
//     }
//     obj->defaultMethod();
//     rwlock->unlock();
//     return;
// }
// ```
// The function can be rewritten with a cleaner and safer implementation using
// a guard object.  The `safeFunc` function is simpler than `errorProneFunc`,
// is exception safe, and avoids the multiple calls to unlock that can be a
// source of errors.
// ```
// static void safeFunc(const my_Object *obj, my_RWLock *rwlock)
// {
//     bslmt::ReadLockGuard<my_RWLock> guard(rwlock);
//     if (someCondition) {
//         obj->someMethod();
//         return;                                                   // RETURN
//     } else if (someOtherCondition) {
//         obj->someOtherMethod();
//         // OK, rwlock is automatically unlocked
//         return;                                                   // RETURN
//     }
//     obj->defaultMethod();
//     return;
// }
// ```
// When blocking while acquiring the lock is not desirable, one may instead use
// a `bslmt::ReadLockGuardTryLock` in the typical following fashion:
// ```
// /// Perform task and return positive value if locking succeeds.  Return
// /// 0 if locking fails.
// static int safeButNonBlockingFunc(const my_Object *obj, my_RWLock *rwlock)
// {
//     const int RETRIES = 1; // use higher values for higher success rate
//     bslmt::ReadLockGuardTryLock<my_RWLock> guard(rwlock, RETRIES);
//     if (guard.ptr()) { // rwlock is locked
//         if (someCondition) {
//             obj->someMethod();
//             return 2;                                             // RETURN
//         } else if (someOtherCondition) {
//             obj->someOtherMethod();
//             return 3;                                             // RETURN
//         }
//         obj->defaultMethod();
//         return 1;                                                 // RETURN
//     }
//     return 0;
// }
// ```
// If the underlying lock object provides an upgrade to a lock for write (as
// does `bslmt::ReaderWriterLock` with the `upgradeToWriteLock` function, for
// example), this can be safely used in conjunction with
// `bslmt::ReadLockGuard`, as long as the same `unlock` method is used to
// release both kinds of locks.  The following method illustrates this usage:
// ```
// static void safeUpdateFunc(my_Object *obj, my_RWLock *rwlock)
// {
//     const my_Object *constObj = obj;
//     bslmt::ReadLockGuard<my_RWLock> guard(rwlock);
//     if (someUpgradeCondition) {
//         rwlock->upgradeToWriteLock();
//         obj->someUpgradeMethod();
//         return;                                                   // RETURN
//     } else if (someOtherCondition) {
//         constObj->someOtherMethod();
//         // OK, rwlock is automatically unlocked
//         return;                                                   // RETURN
//     }
//     constObj->defaultMethod();
//     return;
// }
// ```
// In the above code, the call to `upgradeToWriteLock` is not necessarily
// atomic, as the upgrade may release the lock for read and be interrupted
// before getting a lock for write.  It is possible to guarantee atomicity (as
// does `bslmt::ReaderWriterLock` if the `lockReadReserveWrite` function is
// used instead of `lockRead`, for example), but the standard constructor
// should not be used.  Instead, the `lockReadReserveWrite` lock function
// should be used explicitly, and the guard constructed with an object which is
// already locked.  The following method illustrates this usage:
// ```
// static void safeAtomicUpdateFunc(my_Object *obj, my_RWLock *rwlock)
// {
//     const my_Object *constObj = obj;
//     rwlock->lockReadReserveWrite();
//     const int PRELOCKED = 1;
//     bslmt::ReadLockGuard<my_RWLock> guard(rwlock, PRELOCKED);
//     if (someUpgradeCondition) {
//         rwlock->upgradeToWriteLock();
//         obj->someUpgradeMethod();
//         return;                                                   // RETURN
//     } else if (someOtherCondition) {
//         constObj->someOtherMethod();
//         return;                                                   // RETURN
//     }
//     constObj->defaultMethod();
//     return;
// }
// ```
// Note that in the code above, the function `rwlock->lockRead()` is never
// called, but is nevertheless required for the code to compile.
//
// Instantiations of `bslmt::ReadLockGuardUnlock` can be interleaved with
// instantiations of `bslmt::ReadLockGuard` to create both critical sections
// and regions where the lock is released.
// ```
// void f(my_RWLock *rwlock)
// {
//     bslmt::ReadLockGuard<my_RWLock> guard(rwlock);
//
//     // critical section here
//
//     {
//         bslmt::ReadLockGuardUnlock<my_RWLock> guard(rwlock);
//
//         // rwlock is unlocked here
//
//     } // rwlock is locked again here
//
//     // critical section here
//
// } // rwlock is unlocked here
// ```
// Care must be taken so as not to interleave guard objects in such a way as to
// cause an illegal sequence of calls on a lock (two sequential lock calls or
// two sequential unlock calls on a non-recursive read/write lock).

#include <bslscm_version.h>

namespace BloombergLP {
namespace bslmt {

                           // ===================
                           // class ReadLockGuard
                           // ===================

/// This class template implements a guard for acquisition and release of
/// read synchronization resources (i.e., reader locks).
template <class T>
class ReadLockGuard {

    // DATA
    T *d_lock_p;  // lock guarded by this object (held, not owned)

  private:
    // NOT IMPLEMENTED
    ReadLockGuard(const ReadLockGuard<T>&);
    ReadLockGuard<T>& operator=(const ReadLockGuard<T>&);

  public:
    // CREATORS

    /// Create a scoped guard that conditionally manages the specified
    /// `lock` (if non-null) and invokes `lock->lockRead()`.  Supplying a
    /// null `lock` has no effect.  The behavior is undefined unless `lock`
    /// (if non-null) is not already locked by this thread.  Note that
    /// `lock` must remain valid throughout the lifetime of this guard, or
    /// until `release` is called.
    explicit ReadLockGuard(T *lock);

    /// Create a scoped guard that conditionally manages the specified
    /// `lock` (if non-null) and invokes `lock->lockRead()` if the specified
    /// `alreadyLockedFlag` is `false`.  Supplying a null `lock` has no
    /// effect.  The behavior is undefined unless the state of `lock` (if
    /// non-null) is consistent with `alreadyLockedFlag`.  Note that
    /// `alreadyLockedFlag` is used to indicate whether `lock` is in an
    /// already-locked state when passed, so if `alreadyLockedFlag` is
    /// `true` the `lock` method will *not* be called on the supplied
    /// `lock`.  Also note that `lock` must remain valid throughout the
    /// lifetime of this guard, or until `release` is called.
    ReadLockGuard(T *lock, bool alreadyLockedFlag);

    /// Destroy this scoped guard and invoke the `unlock` method on the
    /// lock object under management by this guard, if any.  If no lock is
    /// currently being managed, this method has no effect.
    ~ReadLockGuard();

    // MANIPULATORS

    /// Return the address of the modifiable lock object under management by
    /// this guard, and release the lock from further management by this
    /// guard.  If no lock is currently being managed, return 0 with no
    /// other effect.  Note that this operation does *not* unlock the lock
    /// object (if any) that was under management.
    T *release();

    // ACCESSORS

    /// Return the address of the modifiable lock object under management by
    /// this guard, or 0 if no lock is currently being managed.
    T *ptr() const;
};

                           // ===================
                           // class LockReadGuard
                           // ===================

/// @DEPRECATED:  Use `ReadLockGuard` instead.
template <class T>
class LockReadGuard : public ReadLockGuard<T> {

  private:
    // NOT IMPLEMENTED
    LockReadGuard(const LockReadGuard<T>&);
    LockReadGuard<T>& operator=(const LockReadGuard<T>&);

  public:
    // CREATORS

    /// @DEPRECATED: Use `ReadLockGuard` instead.
    explicit LockReadGuard(T *lock);

    /// @DEPRECATED: Use `ReadLockGuard` instead.
    LockReadGuard(T *lock, bool alreadyLockedFlag);
};

                        // =========================
                        // class ReadLockGuardUnlock
                        // =========================

/// This class template implements a guard for release and reacquisition
/// of read synchronization resources (i.e., reader locks).
template <class T>
class ReadLockGuardUnlock {

    // DATA
    T *d_lock_p;  // lock guarded by this object (held, not owned)

  private:
    // NOT IMPLEMENTED
    ReadLockGuardUnlock(const ReadLockGuardUnlock<T>&);
    ReadLockGuardUnlock<T>& operator=(const ReadLockGuardUnlock<T>&);

  public:
    // CREATORS

    /// Create a scoped guard that conditionally manages the specified
    /// `lock` (if non-null) and invokes `lock->unlock()`.  Supplying a null
    /// `lock` has no effect.  The behavior is undefined unless `lock` (if
    /// non-null) is locked by this thread.  Note that `lock` must remain
    /// valid throughout the lifetime of this guard, or until `release` is
    /// called.
    explicit ReadLockGuardUnlock(T *lock);

    /// Create a scoped guard that conditionally manages the specified
    /// `lock` (if non-null) and invokes `lock->unlock()` if the specified
    /// `alreadyUnlockedFlag` is `false`.  Supplying a null `lock` has no
    /// effect.  The behavior is undefined unless the state of `lock` (if
    /// non-null) is consistent with `alreadyUnlockedFlag`.  Note that
    /// `alreadyUnlockedFlag` is used to indicate whether `lock` is in an
    /// already-unlocked state when passed, so if `alreadyUnlockedFlag` is
    /// `true` the `unlock` method will *not* be called on the supplied
    /// `lock`.  Also note that `lock` must remain valid throughout the
    /// lifetime of this guard, or until `release` is called.
    ReadLockGuardUnlock(T *lock, bool alreadyUnlockedFlag);

    /// Destroy this scoped guard and invoke the `lockRead` method on the
    /// lock object under management by this guard, if any.  If no lock is
    /// currently being managed, this method has no effect.
    ~ReadLockGuardUnlock();

    // MANIPULATORS

    /// Return the address of the modifiable lock object under management by
    /// this guard, and release the lock from further management by this
    /// guard.  If no lock is currently being managed, return 0 with no
    /// other effect.  Note that this operation does *not* lock the lock
    /// object (if any) that was under management.
    T *release();

    // ACCESSORS

    /// Return the address of the modifiable lock object under management by
    /// this guard, or 0 if no lock is currently being managed.
    T *ptr() const;
};

                        // ==========================
                        // class ReadLockGuardTryLock
                        // ==========================

/// This class template implements a guard for tentative acquisition and
/// release of read synchronization resources (i.e., reader locks).
template <class T>
class ReadLockGuardTryLock {

    // DATA
    T *d_lock_p;  // lock guarded by this object (held, not owned)

  private:
    // NOT IMPLEMENTED
    ReadLockGuardTryLock(const ReadLockGuardTryLock<T>&);
    ReadLockGuardTryLock<T>& operator=(const ReadLockGuardTryLock<T>&);

  public:
    // CREATORS

    /// Create a scoped guard that conditionally manages the specified
    /// `lock` (if non-null) and invokes `lock->tryLockRead()` until the
    /// lock is acquired for reading, or until the optionally specified
    /// `attempts` have been made to acquire the lock.  If `attempts` is not
    /// specified only one attempt is made to acquire the lock.  Supplying a
    /// null `lock` has no effect.  The behavior is undefined unless `lock`
    /// (if non-null) is not already locked by this thread and
    /// `0 < attempts`.  Note that `lock` must remain valid throughout the
    /// lifetime of this guard, or until `release` is called.
    explicit ReadLockGuardTryLock(T *lock, int attempts = 1);

    /// Destroy this scoped guard and invoke the `unlock` method on the
    /// lock object under management by this guard, if any.  If no lock is
    /// currently being managed, this method has no effect.
    ~ReadLockGuardTryLock();

    // MANIPULATORS

    /// Return the address of the modifiable lock object under management by
    /// this guard, and release the lock from further management by this
    /// guard.  If no lock is currently being managed, return 0 with no
    /// other effect.  Note that this operation does *not* unlock the lock
    /// object (if any) that was under management.
    T *release();

    // ACCESSORS

    /// Return the address of the modifiable lock object under management by
    /// this guard, or 0 if no lock is currently being managed.
    T *ptr() const;
};

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

                           // -------------------
                           // class ReadLockGuard
                           // -------------------

// CREATORS
template <class T>
inline
ReadLockGuard<T>::ReadLockGuard(T *lock)
: d_lock_p(lock)
{
    if (d_lock_p) {
        d_lock_p->lockRead();
    }
}

template <class T>
inline
ReadLockGuard<T>::ReadLockGuard(T *lock, bool alreadyLockedFlag)
: d_lock_p(lock)
{
    if (d_lock_p && !alreadyLockedFlag) {
        d_lock_p->lockRead();
    }
}

template <class T>
inline
ReadLockGuard<T>::~ReadLockGuard()
{
    if (d_lock_p) {
        d_lock_p->unlock();
    }
}

// MANIPULATORS
template <class T>
inline
T *ReadLockGuard<T>::release()
{
    T *lock  = d_lock_p;
    d_lock_p = 0;
    return lock;
}

// ACCESSORS
template <class T>
inline
T *ReadLockGuard<T>::ptr() const
{
    return d_lock_p;
}

                           // -------------------
                           // class LockReadGuard
                           // -------------------

// CREATORS
template <class T>
inline
LockReadGuard<T>::LockReadGuard(T *lock)
: ReadLockGuard<T>(lock)
{
}

template <class T>
inline
LockReadGuard<T>::LockReadGuard(T *lock, bool alreadyLockedFlag)
: ReadLockGuard<T>(lock, alreadyLockedFlag)
{
}

                        // -------------------------
                        // class ReadLockGuardUnlock
                        // -------------------------

// CREATORS
template <class T>
inline
ReadLockGuardUnlock<T>::ReadLockGuardUnlock(T *lock)
: d_lock_p(lock)
{
    if (d_lock_p) {
        d_lock_p->unlock();
    }
}

template <class T>
inline
ReadLockGuardUnlock<T>::ReadLockGuardUnlock(T    *lock,
                                                   bool  alreadyUnlockedFlag)
: d_lock_p(lock)
{
    if (d_lock_p && !alreadyUnlockedFlag) {
        d_lock_p->unlock();
    }
}

template <class T>
inline
ReadLockGuardUnlock<T>::~ReadLockGuardUnlock()
{
    if (d_lock_p) {
        d_lock_p->lockRead();
    }
}

// MANIPULATORS
template <class T>
inline
T *ReadLockGuardUnlock<T>::release()
{
    T *lock  = d_lock_p;
    d_lock_p = 0;
    return lock;
}

// ACCESSORS
template <class T>
inline
T *ReadLockGuardUnlock<T>::ptr() const
{
    return d_lock_p;
}

                        // --------------------------
                        // class ReadLockGuardTryLock
                        // --------------------------

// CREATORS
template <class T>
ReadLockGuardTryLock<T>::ReadLockGuardTryLock(T *lock, int attempts)
: d_lock_p(0)
{
    if (lock) {
        while (attempts--) {
            if (!lock->tryLockRead()) {
                d_lock_p = lock;
                break;
            }
        }
    }
}

template <class T>
inline
ReadLockGuardTryLock<T>::~ReadLockGuardTryLock()
{
    if (d_lock_p) {
        d_lock_p->unlock();
    }
}

// MANIPULATORS
template <class T>
inline
T *ReadLockGuardTryLock<T>::release()
{
    T *lock  = d_lock_p;
    d_lock_p = 0;
    return lock;
}

// ACCESSORS
template <class T>
inline
T *ReadLockGuardTryLock<T>::ptr() const
{
    return d_lock_p;
}

}  // close package namespace
}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2015 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 ----------------------------------
