// bslmt_sluice.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_SLUICE
#define INCLUDED_BSLMT_SLUICE

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

//@PURPOSE: Provide a "sluice" class.
//
//@CLASSES:
//  bslmt::Sluice: thread-aware sluice class
//
//@SEE_ALSO: bslmt_conditionimpl_win32
//
//@DESCRIPTION: This component provides a "sluice" class, 'bslmt::Sluice'.  A
// sluice is useful for controlling the release of threads from a common
// synchronization point.  One or more threads may "enter" a 'bslmt::Sluice'
// object (via the 'enter' method), and then wait to be released (via either
// the 'wait' or 'timedWait' method).  Either one waiting thread (via the
// 'signalOne' method), or all waiting threads (via the 'signalAll' method),
// may be signaled for release.  In either case, 'bslmt::Sluice' provides a
// guarantee against starvation; newly-entering threads will not indefinitely
// prevent threads that previously entered from being signaled.
//
///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
///-----
// 'bslmt::Sluice' is intended to be used to implement other synchronization
// mechanisms.  In particular, the functionality provided by 'bslmt::Sluice' is
// useful for implementing a condition variable:
//..
//  class MyCondition {
//      // This class implements a condition variable based on 'bslmt::Sluice'.
//
//      // DATA
//      bslmt::Sluice d_waitSluice;  // sluice object
//
//    public:
//      // MANIPULATORS
//      void wait(bslmt::Mutex *mutex)
//      {
//          const void *token = d_waitSluice.enter();
//          mutex->unlock();
//          d_waitSluice.wait(token);
//          mutex->lock();
//      }
//
//      void signal()
//      {
//          d_waitSluice.signalOne();
//      }
//
//      void broadcast()
//      {
//          d_waitSluice.signalAll();
//      }
//  };
//..

#include <bslscm_version.h>

#include <bslmt_lockguard.h>
#include <bslmt_mutex.h>
#include <bslmt_timedsemaphore.h>

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

#include <bslma_allocator.h>

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
#include <bslmt_chronoutil.h>

#include <bsl_chrono.h>
#endif

namespace BloombergLP {
namespace bslmt {

                               // ============
                               // class Sluice
                               // ============

class Sluice {
    // This class controls the release of threads from a common synchronization
    // point.  One or more threads may "enter" a 'Sluice' object, and then wait
    // to be released.  Either one waiting thread (via the 'signalOne' method),
    // or all waiting threads (via the 'signalAll' method), may be signaled for
    // release.  In any case, 'Sluice' provides a guarantee against starvation.

  private:
    // PRIVATE TYPES
    struct GenerationDescriptor {
        // This object represents one "generation" in a sluice.  A generation
        // begins when a thread enters the sluice, and ends (ceases accepting
        // new entering threads) when 'signalOne' or 'signalAll' is invoked.
        // The last thread in the generation to invoke 'wait' is responsible
        // for returning the descriptor to the pool.

        // DATA
        int                   d_numThreads;   // number of threads entered, but
                                              // not yet finished waiting

        int                   d_numSignaled;  // number of threads signaled,
                                              // but not yet finished waiting

        TimedSemaphore        d_sema;         // semaphore on which to wait

        GenerationDescriptor *d_next;         // pointer to the next free
                                              // descriptor in the pool

        // CREATORS
        explicit GenerationDescriptor(bsls::SystemClockType::Enum clockType);
            // Create a generation descriptor object with the specified
            // 'clockType'.
    };

    // DATA
    Mutex                 d_mutex;               // for synchronizing access to
                                                 // data members

    GenerationDescriptor *d_signaledGeneration;  // generation in which at
                                                 // least one, but not all,
                                                 // threads have been signaled

    GenerationDescriptor *d_pendingGeneration;   // generation in which no
                                                 // threads have been signaled
                                                 // yet

    GenerationDescriptor *d_descriptorPool;      // pool of available
                                                 // generation descriptors

    bsls::SystemClockType::Enum
                          d_clockType;           // the type of clock used for
                                                 // 'absTime' in 'timedWait'

    bslma::Allocator     *d_allocator_p;         // memory allocator (held, not
                                                 // owned)

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

  public:
    // TYPES
    enum { e_TIMED_OUT = TimedSemaphore::e_TIMED_OUT };
        // The value 'timedWait' returns when a timeout occurs.

    // CREATORS
    explicit
    Sluice(bslma::Allocator *basicAllocator = 0);
    explicit
    Sluice(bsls::SystemClockType::Enum  clockType,
           bslma::Allocator            *basicAllocator = 0);
        // Create a sluice.  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
        // documentation).  If 'clockType' is not specified then the realtime
        // system clock is used.  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    explicit
    Sluice(const bsl::chrono::system_clock&,
           bslma::Allocator                 *basicAllocator = 0);
        // Create a sluice.  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).  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.

    explicit
    Sluice(const bsl::chrono::steady_clock&,
           bslma::Allocator                 *basicAllocator = 0);
        // Create a sluice.  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).  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.
#endif

    ~Sluice();
        // Destroy this sluice.

    // MANIPULATORS
    const void *enter();
        // Enter this sluice, and return the token on which the calling thread
        // must subsequently wait.  The behavior is undefined unless 'wait' or
        // 'timedWait' is invoked with the token before this sluice is
        // destroyed.

    void signalAll();
        // Signal all threads that have entered this sluice and have not yet
        // been released.

    void signalOne();
        // Signal one thread that has entered this sluice and has not yet been
        // released.

    int timedWait(const void *token, const bsls::TimeInterval& absTime);
        // Wait for the specified 'token' to be signaled, 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).  Return 0 on success, and
        // 'e_TIMED_OUT' on timeout.  Any other value indicates that an error
        // has occurred.  Errors are unrecoverable.  After an error, the sluice
        // may be destroyed, but any other use has undefined behavior.  The
        // 'token' is released whether or not a timeout occurred.  The behavior
        // is undefined unless 'token' was obtained from a call to 'enter' by
        // this thread, and was not subsequently released (via a call to
        // 'timedWait' or 'wait').

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    template <class CLOCK, class DURATION>
    int timedWait(const void                                      *token,
                  const bsl::chrono::time_point<CLOCK, DURATION>&  absTime);
        // Wait for the specified 'token' to be signaled, 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 associated with the time point.  Return 0 on success,
        // and 'e_TIMED_OUT' on timeout.  Any other value indicates that an
        // error has occurred.  Errors are unrecoverable.  After an error, the
        // sluice may be destroyed, but any other use has undefined behavior.
        // The 'token' is released whether or not a timeout occurred.  The
        // behavior is undefined unless 'token' was obtained from a call to
        // 'enter' by this thread, and was not subsequently released (via a
        // call to 'timedWait' or 'wait').
#endif

    void wait(const void *token);
        // Wait for the specified 'token' to be signaled, and release the
        // 'token'.  The behavior is undefined unless 'token' was obtained from
        // a call to 'enter' by this thread, and was not subsequently released
        // (via a call to 'timedWait' or 'wait').

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

}  // close package namespace

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

                             // ------------
                             // class Sluice
                             // ------------

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
// MANIPULATORS
template <class CLOCK, class DURATION>
int bslmt::Sluice::timedWait(
                    const void                                      *token,
                    const bsl::chrono::time_point<CLOCK, DURATION>&  absTime) {

    GenerationDescriptor *g =
                static_cast<GenerationDescriptor *>(const_cast<void *>(token));

    for (;;) {
        int rc = g->d_sema.timedWait(absTime);

        LockGuard<Mutex> lock(&d_mutex);

        if (g->d_numSignaled) {
            BSLS_ASSERT(d_pendingGeneration != g);

            --g->d_numSignaled;

            rc = 0;
        }
        else if (0 == rc) {
            continue;
        }

        const int numThreads = --g->d_numThreads;

        if (0 == numThreads) {
            // The last thread is responsible for cleanup.

            if (d_signaledGeneration == g) {
                BSLS_ASSERT(0 != rc);
                d_signaledGeneration = 0;
            }

            if (d_pendingGeneration == g) {
                BSLS_ASSERT(0 != rc);
                d_pendingGeneration = 0;
            }

            g->d_next = d_descriptorPool;
            d_descriptorPool = g;
        }
        return rc;                                                    // RETURN
    }
}
#endif

// ACCESSORS
inline
bsls::SystemClockType::Enum bslmt::Sluice::clockType() const
{
    return d_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 ----------------------------------