// bslmt_qlock.h                                                      -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BSLMT_QLOCK
#define INCLUDED_BSLMT_QLOCK

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

//@PURPOSE: Provide small, statically-initializable mutex lock.
//
//@CLASSES:
//       bslmt::QLock: Small, statically-initializable intra-process mutex
//  bslmt::QLockGuard: Automatic locking-unlocking of bslmt::QLock
//
//@SEE_ALSO: bslmt_mutex, bslmt_atomictypes, bslmt_lockguard, bslmt_once
//
//@DESCRIPTION: This component defines a portable and efficient lock for
// ensuring that only one thread at a time enters a specific "critical region"
// -- a section of code that accesses a shared resource -- 'bslmt::Qlock' and
// its associated 'bslmt::QLockGuard'.  The functionality of the 'bslmt::QLock'
// class overlaps those of the 'bslmt::Mutex' and 'bsls::SpinLock' classes, but
// with different usage and performance characteristics, as shown in the
// following grid:
//..
//                                    | QLock | Mutex | SpinLock
// -----------------------------------+-------+-------+---------
// Memory footprint                   | small | large | small
// Cost of construction/destruction   | cheap | costly| cheap
// Statically initializable           | yes   | no    | yes
// Speed at low contention            | fast  | fast  | fast
// Speed at high contention           | slow  | fast  | very slow
// Suitable for long critical regions | yes   | yes   | no
// Fair                               | yes   | no    | no
//..
// The performance trade-offs for a QLock are quite different than those for a
// conventional mutex.  QLocks are best suited for low-contention applications
// where large numbers of locks may be needed.  For example, a node-based data
// structure that needs a lock for each node can benefit from the small size
// and low initialization cost of a QLock compared to that of a conventional
// mutex.  A 'bslmt::Mutex' object cannot be initialized statically because
// some platforms (e.g., Windows XP) do not have a native
// statically-initializable mutex type.  A 'bslmt::QLock' object, in contrast
// is statically initializable on all platforms.
//
// The performance characteristics of a QLock are very similar to those of a
// SpinLock.  However, a QLock is much more suitable than a SpinLock for
// situations where the critical region is more than a few instructions long.
// Also, although QLocks are best for low-contention situations, they do not
// degrade nearly as badly as SpinLocks if there is a lot of contention for the
// lock.  They also use significantly fewer CPU cycles in high-contention
// situations than do SpinLocks.
//
// A unique characteristic of QLocks is that they are fair.  If there is
// contention for a lock, each thread is given the lock in the order in which
// it requested it.  Consequently, every thread competing for the lock will get
// a chance before any other thread can have a second turn; no thread is ever
// "starved" out of the critical region.  This fairness comes at a cost,
// however, in that the scheduler is given less leeway to schedule threads in
// the most efficient manner.
//
///The 'bslmt::QLockGuard' Class
///-----------------------------
// A 'bslmt::QLock' is different from other locking classes such as
// 'bslmt::Mutex' and 'bsls::SpinLock' in that it cannot be manipulated except
// through the auxiliary 'bslmt::QLockGuard' class.  The reason for this
// limited interface is that a QLock requires a small amount of additional
// storage for each thread that is holding or waiting for the lock.  The
// 'bslmt::QLockGuard' provides this extra storage efficiently on the stack.
//
// In typical usage, a 'bslmt::QLockGuard' is created as a local (stack)
// variable, acquires the lock in its constructor and releases the lock in its
// destructor.  If the lock is in use at construction time, then the current
// thread blocks until the lock becomes available.  Although the QLock itself
// is intended to be shared among multiple threads, the guard object must never
// be used by more than one thread at a time.  When multiple threads want to
// acquire the same QLock, each must use its own 'bslmt::QLockGuard' object.
//
// 'bslmt::QLockGuard' also provides the following manipulators typical of
// locking classes:
//..
//  void lock();    // Acquire the lock, waiting if necessary
//  int tryLock();  // Acquire the lock if possible.  Fail if lock is in use.
//  void unlock();  // Free the lock.
//..
// As with other types of mutexes, only one thread may hold the lock at a time.
// Other threads attempting to call 'lock' will block until the lock becomes
// available.  However, it is important to remember that the manipulators
// listed above are only pass-through operations on the shared 'bslmt::QLock'
// object.  In other words, upon return from calling 'lock' on a
// 'bslmt::QLockGuard' object, a thread has actually acquired the lock to the
// underlying 'bslmt::QLock'.
//
// Although it is only a proxy for the actual QLock, 'lock'/'unlock'/'tryLock'
// interface of 'bslmt::QLockGuard' allows it to be treated as though it were
// itself a lock.  In particular, it is possible to instantiate the
// 'bslmt::LockGuard' and 'bslmt::LockGuardUnlock' class templates using
// 'bslmt::QLockGuard'.  This layering of guard classes is useful for creating
// regions where the QLock is locked or unlocked.  For example, if a thread
// acquires a QLock and then needs to temporarily relinquish it, it could use a
// 'bslmt::LockGuardUnlock' as follows:
//..
//  void Node::update()
//  {
//     bslmt::QLockGuard qguard(&d_qlock);  // 'd_qlock' is a 'bslmt::QLock'.
//     readLunarState();
//     if (d_moonIsFull) {
//         // Free lock while we sleep
//         bslmt::LockGuardUnlock<bslmt::QLockGuard> unlock(&qguard)
//         sleep(TWENTY_FOUR_HOURS);
//     }
//     // Lock has been re-acquired
//     ...
//  }
//..
// The behavior is undefined if 'unlock' is invoked from a thread that did not
// successfully acquire the lock, or if 'lock' is called twice in a thread
// without an intervening call to 'unlock' (i.e., 'bslmt::QLockGuard' is
// non-recursive).
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Using 'bslmt::QLock' to Implement a Thread-Safe Singleton
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// For this example, assume that we have the need to use the string "Hello"
// repeatedly in the form of an 'bsl::string' object.  Rather than construct
// the string each time we use it, it would be nice to have only one copy so
// that we can amortize the memory allocation and construction cost over all
// the uses of the string.  It is thus logical to have a single, static
// variable (a singleton) of type 'bsl::string' initialized with the value,
// "Hello".  Unfortunately, as this is a multithreaded application, there is
// the danger that more than one thread will attempt to initialize the
// singleton simultaneously, causing a memory leak at best and memory
// corruption at worse.  To solve this problem, we use a 'bslmt::QLock' to
// synchronize access to the singleton.
//
// We begin by wrapping the singleton in a function:
//..
//  const bsl::string& helloString()
//  {
//..
// This function defines two static variables, a pointer to the singleton, and
// a QLock to control access to the singleton.  Note that both of these
// variables are statically initialized, so there is no need for a run-time
// constructor and hence no danger of a race condition among threads.  The need
// for static initialization is the main reason we choose to use 'bslmt::QLock'
// over 'bslmt::Mutex':
//..
//      static const bsl::string *singletonPtr = 0;
//      static bslmt::QLock qlock = BSLMT_QLOCK_INITIALIZER;
//..
// Before checking the status of the singleton pointer, we must make sure that
// we are not accessing the pointer at the same time that some other thread is
// modifying the pointer.  We do this by acquiring the lock by constructing a
// 'bslmt::QLockGuard' object:
//..
//      bslmt::QLockGuard qlockGuard(&qlock);
//..
// Now we are inside the critical region.  If the pointer has not already been
// set, we can initialize the singleton knowing that no other thread is
// manipulating or accessing these variables at the same time.  Note that this
// critical region involves constructing a variable of type 'bsl::string'.
// This operation, while not ultra-expensive, is too lengthy for comfortably
// holding a spinlock.  Again, the characteristics of 'bslmt::QLock' are
// superior to the alternatives for this application.  (It is worth noting that
// the QLock concept was created specifically to permit this kind of one-time
// processing.  See also 'bslmt_once'.)
//..
//      if (! singletonPtr) {
//          static bsl::string singleton("Hello");
//          singletonPtr = &singleton;
//      }
//..
// Finally, we return a reference to the singleton.  The destructor for
// 'bslmt::QLockGuard' will automatically unlock the QLock and allow another
// thread into the critical region.
//..
//      return *singletonPtr;
//  }
//..
// The following test program shows how our singleton function can be called.
// Note that 'hello1' and 'hello2' have the same address, demonstrating that
// there was only one string created.
//..
//  int usageExample1()
//  {
//      const bsl::string EXPECTED("Hello");
//
//      const bsl::string& hello1 = helloString();
//      assert(hello1 == EXPECTED);
//
//      const bsl::string& hello2 = helloString();
//      assert(hello2  == EXPECTED);
//      assert(&hello2 == &hello1);
//
//      return 0;
//  }
//..

#include <bslscm_version.h>

#include <bsls_atomic.h>
#include <bsls_atomicoperations.h>
#include <bsls_assert.h>

namespace BloombergLP {
namespace bslmt {

class Semaphore;

#define BSLMT_QLOCK_INITIALIZER  { {0} }
    // Use this macro as the value for initializing an object of type 'QLock'
    // For example:
    //..
    //  QLock mylock = BSLMT_QLOCK_INITIALIZER;
    //..

                               // ============
                               // struct QLock
                               // ============

struct QLock {
    // An efficient statically-initializable synchronization primitive that
    // enables serialized access to shared resources.  Objects of this class
    // can only be manipulated through the use of a 'QLockGuard'.  The
    // following idiom is used to initialize objects of type 'QLock':
    //..
    //  QLock mylock = BSLMT_QLOCK_INITIALIZER;
    //..

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

    // We would like to prohibit copy construction, but then this class would
    // not be a POD and we would lose the ability to initialize objects of this
    // class statically.

    // QLock(const QLock&);

  public:
    bsls::AtomicOperations::AtomicTypes::Pointer d_guardQueueTail;
        // Pointer to the last guard in the queue of guards waiting for this
        // lock, or 0 if the lock is unlocked.
        //
        // Note that the first guard in the queue owns the lock so that
        // 'd_guardQueueTail' points to the owner of the lock when the lock is
        // locked and there are no additional guards waiting.
        //
        // It would have been preferable for this member to be private, but
        // then this class would not be statically initializable.  Also, it
        // would have been preferable to make this member an instance of
        // 'bsls::AtomicPointer<>', but again, we would lose the ability to
        // initialize statically.

  public:
    // MANIPULATORS
    void initialize();
        // Set this lock into the initial unlocked state.

    // ACCESSORS
    bool isLocked() const;
        // Return true if this lock is locked and false otherwise.
};

                          // =====================
                          // class QLock_EventFlag
                          // =====================

class QLock_EventFlag {
    // [!PRIVATE!] This class provides a thread-safe mechanism for one thread
    // to inform another thread that some event has occurred.  A flag provides
    // two primary manipulators, 'set', which indicates the event has occurred,
    // and 'waitUntilSet', which waits until that event has occurred (or
    // returns immediately if it has already occurred).  A flag is intended to
    // be used by only two threads: a thread setting the flag, and a thread
    // waiting for the flag to be set, and the behavior is undefined if 'set'
    // is called while the flag is already set, or if 'waitUntilSet' is called
    // while another thread is waiting for the flag.
    //
    // This class is an implementation detail of the 'bslmt_qlock', and must
    // not be used by client code.

  private:
    // PRIVATE TYPES
    typedef bsls::AtomicPointer<Semaphore> AtomicSemaphorePtr;

    // DATA
    AtomicSemaphorePtr d_status;  // status of this flag

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

  public:

    // CREATORS
    QLock_EventFlag();
        // Create an unset flag.

    ~QLock_EventFlag();
        // Destroy this flag.  The behavior is undefined if a thread is
        // currently waiting for the flag to be set.

    // MANIPULATORS
    void reset();
        // Reset this flag to the unset state.  The behavior is undefined if a
        // thread is waiting for this flag to be set.

    void set();
        // Set this flag, and if a thread is waiting for it, signal the waiting
        // thread.  The behavior is undefined if this flag is already set.

    void waitUntilSet(int spinRetryCount);
        // Wait until this flag has been set (returning immediately if this
        // flag is already set), and, if this flag is not already set, spin for
        // the specified 'spinCount' iterations before waiting on a semaphore.
        // The behavior is undefined unless there are no other threads waiting
        // for this flag to be set.
};

                             // ================
                             // class QLockGuard
                             // ================

class QLockGuard  {
    // This class provides the means to acquire and release the lock on a
    // 'QLock' object.  Typically, the lock is acquired at construction and
    // released automatically on destruction.  This class also provides
    // explicit 'lock', 'tryLock', and 'unlock' primitives.

  private:

    QLock           *d_qlock_p;    // points to tail of the queue.

    QLockGuard      *d_next;       // next object in queue.

    QLock_EventFlag  d_readyFlag;  // flag indicating when the lock is released
                                   // by its predecessor in the queue

    QLock_EventFlag  d_nextFlag;   // flag indicating 'd_next' is set by its
                                   // successor.

    bool             d_locked;     // 'true' if this guard holds the lock

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

    // PRIVATE MANIPULATORS
    void unlockRaw();
        // Free the lock but do not clear the member variables of this class.

  public:
    // CREATORS
    QLockGuard();
        // Create a guard in the unlocked state, not associated with any
        // 'QLock' objects.

    explicit QLockGuard(QLock *qlock, bool doLock = true);
        // Create a guard associated with the specified 'qlock'.  Acquire the
        // lock unless the (optionally) specified 'doLock' is false.  If the
        // 'lock' is not free, block until it can be acquired.

    ~QLockGuard();
        // Destroy this object.  If this object holds a lock, automatically
        // free it.

    // MANIPULATORS
    void setQLock(QLock *qlock);
        // Associate this guard with the specified 'qlock'.  The behavior is
        // undefined if this object is already in a locked state.

    void lock();
        // Acquire a lock on the associated 'QLock' object.  If the 'lock' is
        // not free, block until it can be acquired.  The behavior is undefined
        // if the calling thread already owns the lock on the QLock.

    void lock(QLock *qlock);
        // Associate this guard with the specified 'qlock' and acquire the
        // lock.  If the 'lock' is not free, block until it can be acquired.
        // The behavior is undefined if the calling thread already owns the
        // lock on 'qlock' or if this object is in the locked state.

    int tryLock();
        // Attempt to acquire a lock on the associated 'QLock' object.  Return
        // 0 on success, a positive value of the associated QLock object is
        // already locked, or a negative value if an error occurs.

    void unlock();
        // Release the lock on the associated 'QLock'.  The behavior is
        // undefined unless this guard previously acquired the lock and has not
        // already released it.
};

}  // close package namespace

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

                               // -----------
                               // class QLock
                               // -----------

// MANIPULATORS
inline
void bslmt::QLock::initialize()
{
    bsls::AtomicOperations::setPtrRelaxed(&d_guardQueueTail, 0);
}

// ACCESSORS
inline
bool bslmt::QLock::isLocked() const
{
    return bsls::AtomicOperations::getPtr(&d_guardQueueTail) != 0;
}

                          // ---------------------
                          // class QLock_EventFlag
                          // ---------------------

// CREATORS
inline
bslmt::QLock_EventFlag::QLock_EventFlag()
: d_status(0)
{
}

inline
bslmt::QLock_EventFlag::~QLock_EventFlag()
{
}

// MANIPULATORS
inline
void bslmt::QLock_EventFlag::reset()
{
    d_status = 0;
}

                             // ----------------
                             // class QLockGuard
                             // ----------------

// CREATORS
inline
bslmt::QLockGuard::QLockGuard()
: d_qlock_p   (0)
, d_next      (0)
, d_readyFlag ()
, d_nextFlag  ()
, d_locked    (false)
{
}

inline
bslmt::QLockGuard::QLockGuard(QLock *qlock, bool doLock)
: d_qlock_p   (qlock)
, d_next      (0)
, d_readyFlag ()
, d_nextFlag  ()
, d_locked    (false)
{
    if (doLock) {
        lock();
    }
}

inline
bslmt::QLockGuard::~QLockGuard()
{
    if (d_locked) {
        unlockRaw();
    }
}

// MANIPULATORS
inline
void bslmt::QLockGuard::setQLock(QLock *qlock)
{
    BSLS_ASSERT_SAFE(!d_locked);

    d_qlock_p = qlock;
}

inline
void bslmt::QLockGuard::lock(QLock *qlock)
{
    BSLS_ASSERT_SAFE(!d_locked);
    BSLS_ASSERT_SAFE(qlock);

    d_qlock_p = qlock;
    lock();
}

inline
void bslmt::QLockGuard::unlock()
{
    if (d_locked) {
        // Release the lock, and reset the state variables so it can be
        // relocked.

        unlockRaw();

        d_locked = false;
        d_next   = 0;
        d_readyFlag.reset();
        d_nextFlag.reset();
    }
}

}  // 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 ----------------------------------