// bslmt_timedsemaphore.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_TIMEDSEMAPHORE
#define INCLUDED_BSLMT_TIMEDSEMAPHORE

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

//@PURPOSE: Provide a timed semaphore class.
//
//@CLASSES:
//  bslmt::TimedSemaphore: timed semaphore class
//
//@SEE_ALSO: bslmt_semaphore
//
//@DESCRIPTION: This component defines a portable and efficient thread
// synchronization primitive.  In particular, 'bslmt::TimedSemaphore' is an
// efficient synchronization primitive that enables sharing of a counted number
// of resources or exclusive access.
//
// 'bslmt::TimedSemaphore' differs from 'bslmt::Semaphore' in that the former
// supports a 'timedWait' method, whereas the latter does not.  In addition,
// 'bslmt::Semaphore' has a 'getValue' accessor, whereas
// 'bslmt::TimedSemaphore' does not.  In the case of the timed semaphore,
// 'getValue' cannot be implemented efficiently on all platforms, so that
// method is *intentionally* not provided.
//
///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)'.
//
// On platforms that support 'bsl::chrono', there are constructors that take
// 'bsl::chrono'-style clocks.  If the clock type indicated at construction is
// 'bsl::chrono::system_clock', then the results will be the same as if
// 'bsls::SystemClockType::e_REALTIME' was indicated.  If the clock type
// indicated at construction is 'bsl::chrono::steady_clock', then the results
// will be the same as if 'bsls::SystemClockType::e_MONOTONIC' was indicated.
// Constructing from a user-defined clock is not supported.
//
///Usage
///-----
// This example illustrates a very simple queue where potential clients can
// push integers to a queue, and later retrieve the integer values from the
// queue in FIFO order.  It illustrates two potential uses of semaphores: to
// enforce exclusive access, and to allow resource sharing.  This queue allows
// clients to set a limit on how long they wait to retrieve values.
//..
//  class IntQueue {
//      // FIFO queue of integer values.
//
//      // DATA
//      bsl::deque<int>       d_queue;       // underlying queue
//      bslmt::TimedSemaphore d_resourceSem; // resource-availability semaphore
//      bslmt::TimedSemaphore d_mutexSem;    // mutual-access semaphore
//
//      // NOT IMPLEMENTED
//      IntQueue(const IntQueue&);
//      IntQueue& operator=(const IntQueue&);
//
//    public:
//      // CREATORS
//      explicit IntQueue(bslma::Allocator *basicAllocator = 0);
//          // Create an 'IntQueue' object.  Optionally specified a
//          // 'basicAllocator' used to supply memory.  If 'basicAllocator' is
//          // 0, the currently installed default allocator is used.
//
//      ~IntQueue();
//          // Destroy this 'IntQueue' object.
//
//      // MANIPULATORS
//      int getInt(int *result, int maxWaitSeconds = 0);
//          // Load the first integer in this queue into the specified 'result'
//          // and return 0 unless the operation takes more than the optionally
//          // specified 'maxWaitSeconds', in which case return a nonzero value
//          // and leave 'result' unmodified.
//
//      void pushInt(int value);
//          // Push the specified 'value' to this 'IntQueue' object.
//  };
//..
// Note that the 'IntQueue' constructor increments the count of the semaphore
// to 1 so that values can be pushed into the queue immediately following
// construction:
//..
//  // CREATORS
//  IntQueue::IntQueue(bslma::Allocator *basicAllocator)
//  : d_queue(basicAllocator)
//  , d_resourceSem(bsls::SystemClockType::e_MONOTONIC)
//  {
//      d_mutexSem.post();
//  }
//
//  IntQueue::~IntQueue()
//  {
//      d_mutexSem.wait();  // Wait for potential modifier.
//  }
//
//  // MANIPULATORS
//  int IntQueue::getInt(int *result, int maxWaitSeconds)
//  {
//      // Waiting for resources.
//      if (0 == maxWaitSeconds) {
//          d_resourceSem.wait();
//      } else {
//          bsls::TimeInterval absTime = bsls::SystemTime::nowMonotonicClock()
//              .addSeconds(maxWaitSeconds);
//          int rc = d_resourceSem.timedWait(absTime);
//          if (0 != rc) {
//             return rc;
//          }
//      }
//
//      // 'd_mutexSem' is used for exclusive access.
//      d_mutexSem.wait();       // lock
//      *result = d_queue.back();
//      d_queue.pop_back();
//      d_mutexSem.post();       // unlock
//
//      return 0;
//  }
//
//  void IntQueue::pushInt(int value)
//  {
//      d_mutexSem.wait();
//      d_queue.pushFront(value);
//      d_mutexSem.post();
//
//      d_resourceSem.post();  // Signal that we have resources available.
//  }
//..

#include <bslscm_version.h>

#include <bslmt_chronoutil.h>
#include <bslmt_platform.h>
#include <bslmt_timedsemaphoreimpl_posixadv.h>
#include <bslmt_timedsemaphoreimpl_pthread.h>
#include <bslmt_timedsemaphoreimpl_win32.h>

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

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
#include <bsl_chrono.h>
#endif

namespace BloombergLP {
namespace bslmt {

template <class TIMED_SEMAPHORE_POLICY>
class TimedSemaphoreImpl;

                           // ====================
                           // class TimedSemaphore
                           // ====================

class TimedSemaphore {
    // This class implements a portable timed semaphore type for thread
    // synchronization.  It forwards all requests to an appropriate
    // platform-specific implementation.

    // DATA
    TimedSemaphoreImpl<Platform::TimedSemaphorePolicy> d_impl;
                                            // platform-specific implementation
    // NOT IMPLEMENTED
    TimedSemaphore(const TimedSemaphore&);
    TimedSemaphore& operator=(const TimedSemaphore&);

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

    // CREATORS
    explicit
    TimedSemaphore(
    bsls::SystemClockType::Enum clockType = bsls::SystemClockType::e_REALTIME);
        // Create a timed semaphore initially having a count of 0.  Optionally
        // specify a 'clockType' indicating the type of the system clock
        // against which the 'absTime' timeouts passed to the 'timedWait'
        // methods 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
    TimedSemaphore(const bsl::chrono::system_clock&);
        // Create a timed semaphore initially having a count of 0.  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
    TimedSemaphore(const bsl::chrono::steady_clock&);
        // Create a timed semaphore initially having a count of 0.  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

    explicit
    TimedSemaphore(
    int                         count,
    bsls::SystemClockType::Enum clockType = bsls::SystemClockType::e_REALTIME);
        // Create a timed semaphore initially having the specified 'count'.
        // Optionally specify a 'clockType' indicating the type of the system
        // clock against which the 'absTime' timeouts passed to the 'timedWait'
        // methods 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.  The behavior is undefined
        // unless '0 <= count'.

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    TimedSemaphore(int count, const bsl::chrono::system_clock&);
        // Create a timed semaphore initially having the specified 'count'.
        // 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).
        // The behavior is undefined unless '0 <= count'.

    TimedSemaphore(int count, const bsl::chrono::steady_clock&);
        // Create a timed semaphore initially having the specified 'count'.
        // 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).
        // The behavior is undefined unless '0 <= count'.
#endif

    ~TimedSemaphore();
        // Destroy this timed semaphore.

    // MANIPULATORS
    void post();
        // Atomically increment the count of this timed semaphore.

    void post(int value);
        // Atomically increase the count of this timed semaphore by the
        // specified 'value'.  The behavior is undefined unless 'value > 0'.

    int timedWait(const bsls::TimeInterval& absTime);
        // Block until the count of this semaphore is a positive value, or
        // until the specified 'absTime' timeout expires.  '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).  If the
        // 'absTime' timeout did not expire before the count attained a
        // positive value, atomically decrement the count and return 0.  If the
        // 'absTime' timeout did expire, return 'e_TIMED_OUT' with no effect
        // on the count.  Any other value indicates that an error has occurred.
        // Errors are unrecoverable.  After an error, the semaphore may be
        // destroyed, but any other use has undefined behavior.  On Windows
        // platforms, this method may return 'e_TIMED_OUT' slightly before
        // 'absTime'.

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    template <class CLOCK, class DURATION>
    int timedWait(const bsl::chrono::time_point<CLOCK, DURATION>& absTime);
        // Block until the count of this semaphore is a positive value, or
        // until the specified 'absTime' timeout expires.  'absTime' is an
        // *absolute* time represented by a time point with respect to some
        // epoch, which is determined by the clock associated with the time
        // point.  If the 'absTime' timeout did not expire before the count
        // attained a positive value, atomically decrement the count and return
        // 0.  If the 'absTime' timeout did expire, return 'e_TIMED_OUT' with
        // no effect on the count.  Any other value indicates that an error has
        // occurred.  Errors are unrecoverable.  After an error, the semaphore
        // may be destroyed, but any other use has undefined behavior.  On
        // Windows platforms, this method may return 'e_TIMED_OUT' slightly
        // before 'absTime'.
#endif

    int tryWait();
        // If the count of this timed semaphore is positive, atomically
        // decrement the count and return 0; otherwise, return a non-zero value
        // with no effect on the count.

    void wait();
        // Block until the count of this timed semaphore is a positive value,
        // then atomically decrement the count and return.

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

}  // close package namespace

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

                           // --------------------
                           // class TimedSemaphore
                           // --------------------

// CREATORS
inline
bslmt::TimedSemaphore::TimedSemaphore(bsls::SystemClockType::Enum clockType)
: d_impl(clockType)
{
}

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

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

inline
bslmt::TimedSemaphore::TimedSemaphore(int                         count,
                                      bsls::SystemClockType::Enum clockType)
: d_impl(count, clockType)
{
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
inline
bslmt::TimedSemaphore::TimedSemaphore(int                              count,
                                      const bsl::chrono::system_clock&)
: d_impl(count, bsls::SystemClockType::e_REALTIME)
{
}

inline
bslmt::TimedSemaphore::TimedSemaphore(int                              count,
                                      const bsl::chrono::steady_clock&)
: d_impl(count, bsls::SystemClockType::e_MONOTONIC)
{
}
#endif

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

// MANIPULATORS
inline
void bslmt::TimedSemaphore::post()
{
    d_impl.post();
}

inline
void bslmt::TimedSemaphore::post(int value)
{
    d_impl.post(value);
}

inline
int bslmt::TimedSemaphore::timedWait(const bsls::TimeInterval& absTime)
{
    return d_impl.timedWait(absTime);
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
template <class CLOCK, class DURATION>
inline
int bslmt::TimedSemaphore::timedWait(
                       const bsl::chrono::time_point<CLOCK, DURATION>& absTime)
{
    return bslmt::ChronoUtil::timedWait(this, absTime);
}
#endif

inline
int bslmt::TimedSemaphore::tryWait()
{
    return d_impl.tryWait();
}

inline
void bslmt::TimedSemaphore::wait()
{
    d_impl.wait();
}

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

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