// bslmt_condition.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_CONDITION
#define INCLUDED_BSLMT_CONDITION

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

//@PURPOSE: Provide a portable, efficient condition variable.
//
//@CLASSES:
//  bslmt::Condition: portable intra-process signaling mechanism
//
//@SEE_ALSO: bslmt_mutex
//
//@DESCRIPTION: The 'bslmt::Condition' class provided by this component
// implements the concept of a *condition* *variable*, enabling multiple
// threads to communicate information about the state of shared data.  A
// condition variable is a signaling mechanism associated with a mutex, which
// in turn protects a data invariant.  A condition variable enables threads to
// wait for a predicate (i.e., logical expression) to become true, and to
// communicate to other threads that the predicate might be true.
//
// One or more threads can wait efficiently on a condition variable, either
// indefinitely or until some *absolute* time, by invoking one of the following
// methods of 'bslmt::Condition':
//..
//  int wait(bslmt::Mutex *mutex);
//  int timedWait(bslmt::Mutex *mutex, const bsls::TimeInterval& absTime);
//..
// The caller must lock the mutex before invoking these functions.  The
// 'bslmt::Condition' atomically releases the lock and waits, thereby
// preventing other threads from changing the predicate after the lock is
// released, but before the thread begins to wait.  The 'bslmt' package
// guarantees that this lock will be reacquired before returning from a call to
// the 'wait' and 'timedWait' methods, unless an error occurs.
//
// When invoking the 'timedWait' method, clients must specify, via the
// parameter 'absTime', a timeout after which the call will return even if the
// condition is not signaled.  'absTime' is expressed as a 'bsls::TimeInterval'
// object that holds an *absolute* time according to the clock type the
// 'bslmt::Condition' object is constructed with (the default clock is
// 'bsls::SystemClockType::e_REALTIME').  Clients should use the
// 'bsls::SystemTime::now(clockType)' utility method to obtain the current
// time.
//
// Other threads can indicate that the predicate is true by signaling or
// broadcasting the same 'bslmt::Condition' object.  A broadcast wakes up all
// waiting threads, whereas a signal wakes only one thread.  The client has no
// control over which thread will be signaled if multiple threads are waiting:
//..
//  void signal();
//  void broadcast();
//..
// A thread waiting on a condition variable may be signaled (i.e., the thread
// may wake up without an error), but find that the predicate is still false.
// This situation can arise for a few reasons: spurious wakeups produced by the
// operating system, intercepted wakeups, and loose predicates.  Therefore, a
// waiting thread should always check the predicate *after* (as well as before)
// the call to the 'wait' function.
//
///Supported Clock-Types
///---------------------
// 'bsls::SystemClockType' supplies the enumeration indicating the system clock
// on which timeouts supplied to other methods should be based.  If the clock
// type indicated at construction is 'bsls::SystemClockType::e_REALTIME', the
// 'absTime' argument passed to the 'timedWait' method should be expressed as
// an *absolute* offset since 00:00:00 UTC, January 1, 1970 (which matches the
// epoch used in 'bsls::SystemTime::now(bsls::SystemClockType::e_REALTIME)'.
// If the clock type indicated at construction is
// 'bsls::SystemClockType::e_MONOTONIC', the 'absTime' argument passed to the
// 'timedWait' method should be expressed as an *absolute* offset since the
// epoch of this clock (which matches the epoch used in
// 'bsls::SystemTime::now(bsls::SystemClockType::e_MONOTONIC)'.
//
///Usage
///-----
// Suppose we have a 'bslmt::Condition' object, 'condition', and a boolean
// predicate associated with 'condition' (represented here as a free function
// that returns a 'bool' value):
//..
//  bool predicate()
//      // Return 'true' if the invariant holds for 'condition', and 'false'
//      // otherwise.
//  {
//      return true;
//  }
//..
// The following usage pattern should always be followed:
//..
//    // ...
//
//    bslmt::Condition condition;
//    bslmt::Mutex     mutex;
//
//    mutex.lock();
//    while (false == predicate()) {
//        condition.wait(&mutex);
//    }
//
//    // Modify shared resources and adjust the predicate here.
//
//    mutex.unlock();
//
//    // ...
//..
// The usage pattern for a timed wait is similar, but has extra branches to
// handle a timeout:
//..
//    // ...
//
//    enum { e_TIMED_OUT = -1 };
//    bsls::TimeInterval absTime = bsls::SystemTime::nowRealtimeClock();
//
//    // Advance 'absTime' to some delta into the future here.
//
//    mutex.lock();
//    while (false == predicate()) {
//        const int status = condition.timedWait(&mutex, absTime);
//        if (e_TIMED_OUT == status) {
//            break;
//        }
//    }
//
//    if (false == predicate()) {
//        // The wait timed out and 'predicate' returned 'false'.  Perform
//        // timeout logic here.
//
//        // ...
//    }
//    else {
//        // The condition variable was either signaled or timed out and
//        // 'predicate' returned 'true'.  Modify shared resources and adjust
//        // predicate here.
//
//        // ...
//    }
//    mutex.unlock();
//
//    // ...
//..

#include <bslscm_version.h>

#include <bslmt_conditionimpl_pthread.h>
#include <bslmt_conditionimpl_win32.h>
#include <bslmt_platform.h>

#include <bsls_assert.h>
#include <bsls_libraryfeatures.h>
#include <bsls_timeinterval.h>
#include <bsls_systemclocktype.h>

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
#include <bslmt_chronoutil.h>

#include <bsl_chrono.h>
#endif

namespace BloombergLP {
namespace bslmt {

template <class THREAD_POLICY>
class ConditionImpl;

class Mutex;

                             // ===============
                             // class Condition
                             // ===============

class Condition {
    // This 'class' implements a portable inter-thread signaling primitive.

    // DATA
    ConditionImpl<Platform::ThreadPolicy> d_imp;  // platform-specific
                                                  // implementation

    // NOT IMPLEMENTED
    Condition(const Condition&);
    Condition& operator=(const Condition&);

  public:
    // TYPES
    enum { e_TIMED_OUT = ConditionImpl<Platform::ThreadPolicy>::e_TIMED_OUT };
        // The value 'timedWait' returns when a timeout occurs.

    // CREATORS
    explicit
    Condition(bsls::SystemClockType::Enum clockType =
                                            bsls::SystemClockType::e_REALTIME);
        // Create a condition variable object.  Optionally specify a
        // 'clockType' indicating the type of the system clock against which
        // the 'bsls::TimeInterval' 'absTime' timeouts passed to the
        // 'timedWait' method are to be interpreted (see {Supported
        // Clock-Types} in the component-level documentation).  If 'clockType'
        // is not specified then the realtime system clock is used.

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    explicit
    Condition(const bsl::chrono::system_clock&);
        // Create a condition variable object.  Use the realtime system clock
        // as the clock against which the 'absTime' timeouts passed to the
        // 'timedWait' methods are interpreted (see {Supported Clock-Types} in
        // the component-level documentation).

    explicit
    Condition(const bsl::chrono::steady_clock&);
        // Create a condition variable object.  Use the monotonic system clock
        // as the clock against which the 'absTime' timeouts passed to the
        // 'timedWait' methods are interpreted (see {Supported Clock-Types} in
        // the component-level documentation).
#endif

    ~Condition();
        // Destroy this condition variable object.

    // MANIPULATORS
    void broadcast();
        // Signal this condition variable object by waking up *all* threads
        // that are currently waiting on this condition.  If there are no
        // threads waiting on this condition, this method has no effect.

    void signal();
        // Signal this condition variable object by waking up a single thread
        // that is currently waiting on this condition.  If there are no
        // threads waiting on this condition, this method has no effect.

    int timedWait(Mutex *mutex, const bsls::TimeInterval& absTime);
        // Atomically unlock the specified 'mutex' and suspend execution of the
        // current thread until this condition object is "signaled" (i.e., one
        // of the 'signal' or 'broadcast' methods is invoked on this object) or
        // until the specified 'absTime' timeout expires, then re-acquire a
        // lock on the 'mutex'.  'absTime' is an *absolute* time represented as
        // an interval from some epoch, which is determined by the clock
        // indicated at construction (see {Supported Clock-Types} in the
        // component-level documentation), and is the earliest time at which
        // the timeout may occur.  The 'mutex' remains locked by the calling
        // thread upon returning from this function.  Return 0 on success, and
        // 'e_TIMED_OUT' on timeout.  Any other value indicates that an error
        //  has occurred.  After an error, the condition may be destroyed, but
        // any other use has undefined behavior.  The behavior is undefined
        // unless 'mutex' is locked by the calling thread prior to calling this
        // method.  Note that spurious wakeups are rare but possible, i.e.,
        // this method may succeed (return 0) and return control to the thread
        // without the condition object being signaled.  Also note that the
        // actual time of the timeout depends on many factors including system
        // scheduling and system timer resolution, and may be significantly
        // later than the time requested.

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    template <class CLOCK, class DURATION>
    int timedWait(Mutex                                           *mutex,
                  const bsl::chrono::time_point<CLOCK, DURATION>&  absTime);
        // Atomically unlock the specified 'mutex' and suspend execution of the
        // current thread until this condition object is "signaled" (i.e., one
        // of the 'signal' or 'broadcast' methods is invoked on this object) or
        // until the specified 'absTime' timeout expires, then re-acquire a
        // lock on the 'mutex'.  'absTime' is an *absolute* time represented as
        // an interval from some epoch, which is determined by the clock
        // associated with the time point, and is the earliest time at which
        // the timeout may occur.  The 'mutex' remains locked by the calling
        // thread upon returning from this function.  Return 0 on success, and
        // 'e_TIMED_OUT' on timeout.  Any other value indicates that an error
        // has occurred.  After an error, the condition may be destroyed, but
        // any other use has undefined behavior.  The behavior is undefined
        // unless 'mutex' is locked by the calling thread prior to calling this
        // method.  Note that spurious wakeups are rare but possible, i.e.,
        // this method may succeed (return 0) and return control to the thread
        // without the condition object being signaled.  Also note that the
        // actual time of the timeout depends on many factors including system
        // scheduling and system timer resolution, and may be significantly
        // later than the time requested.  Also note that the lock on 'mutex'
        // may be released and reacquired more than once before this method
        // returns.
#endif

    int wait(Mutex *mutex);
        // Atomically unlock the specified 'mutex' and suspend execution of the
        // current thread until this condition object is "signaled" (i.e.,
        // either 'signal' or 'broadcast' is invoked on this object in another
        // thread), then re-acquire a lock on the 'mutex'.  Return 0 on
        // success, and a non-zero value otherwise.  Spurious wakeups are rare
        // but possible; i.e., this method may succeed (return 0), and return
        // control to the thread without the condition object being signaled.
        // The behavior is undefined unless 'mutex' is locked by the calling
        // thread prior to calling this method.  Note that 'mutex' remains
        // locked by the calling thread upon return from this function.

    // ACCESSORS
    bsls::SystemClockType::Enum clockType() const;
        // Return the clock type used for timeouts.
};

}  // close package namespace

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

                             // ---------------
                             // class Condition
                             // ---------------

// CREATORS
inline
bslmt::Condition::Condition(bsls::SystemClockType::Enum clockType)
: d_imp(clockType)
{
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
inline
bslmt::Condition::Condition(const bsl::chrono::system_clock&)
: d_imp(bsls::SystemClockType::e_REALTIME)
{
}

inline
bslmt::Condition::Condition(const bsl::chrono::steady_clock&)
: d_imp(bsls::SystemClockType::e_MONOTONIC)
{
}
#endif

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

// MANIPULATORS
inline
void bslmt::Condition::broadcast()
{
    d_imp.broadcast();
}

inline
void bslmt::Condition::signal()
{
    d_imp.signal();
}

inline
int bslmt::Condition::timedWait(Mutex                     *mutex,
                                const bsls::TimeInterval&  absTime)
{
    BSLS_ASSERT_SAFE(mutex);

    return d_imp.timedWait(mutex, absTime);
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
template <class CLOCK, class DURATION>
inline
int bslmt::Condition::timedWait(
                      Mutex                                           *mutex,
                      const bsl::chrono::time_point<CLOCK, DURATION>&  absTime)
{
    BSLS_ASSERT_SAFE(mutex);

    return bslmt::ChronoUtil::timedWait(this, mutex, absTime);
}
#endif

inline
int bslmt::Condition::wait(Mutex *mutex)
{
    BSLS_ASSERT_SAFE(mutex);

    return d_imp.wait(mutex);
}

// ACCESSORS
inline
bsls::SystemClockType::Enum bslmt::Condition::clockType() const
{
    return d_imp.clockType();
}

}  // close enterprise namespace

#endif

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