// bslmt_turnstile.h                                                  -*-C++-*-

#ifndef INCLUDED_BSLMT_TURNSTILE
#define INCLUDED_BSLMT_TURNSTILE

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

//@PURPOSE: Provide a mechanism to meter time.
//
//@CLASSES:
//  bslmt::Turnstile: mechanism to meter time
//
//@DESCRIPTION: This component provides a mechanism, `bslmt::Turnstile`, to
// meter time.  A turnstile is configured with a rate that specified how many
// "events" per second the turnstile should allow.  After the rate is set (via
// the constructor or the `reset` method), callers may execute the `waitTurn`
// method, which blocks until the next interval arrives.  If the turnstile is
// not called at or above the configured rate (e.g., due to processing
// performed at each interval), the turnstile is said to be "lagging behind."
// The amount of lag time is obtained from the `lagTime` method.
//
///Comparison with Sleep
///---------------------
// A straightforward implementation of metering is to call some form of sleep
// (e.g., `bslmt::ThreadUtil::microSleep`) with a computed rate after each
// processing step.  However, simply calling "sleep" accumulates errors since
// this implementation does not account for the time taken during the
// processing step.  For example, given two functions that take `rate` (turns
// per second) and `duration` (expected execution time in seconds), and execute
// `rate * duration` calls to `bsl::sqrt`, calling `waitTurn` on a turnstile or
// `bslmt::ThreadUtil::microSleep` with duration `1000000 / rate`,
// respectively; the elapsed time for each call results in the following table,
// showing that the `bslmt::Turnstile` implementation maintains the correct
// rate while the `microSleep` implementation accumulates errors.
// ```
//                    Elapsed Time
// Rate  Duration  Turnstile     Sleep
// ----  --------  ---------     -----
//   10         1   0.900310  0.940390
//  100         1   0.980853  1.609041
//  500         1   1.000711  4.989093
// 1000         1   1.000103  9.988734
// ```
//
///Thread Safety
///-------------
// Except for the `reset` method, this component is thread-safe and
// thread-aware, meaning that multiple threads may safely use their own
// instances or a shared instance of a `bslmt::Turnstile` object, provided that
// `reset` is not called on a turnstile object while another thread is
// accessing or modifying the same object.
//
///Timer Resolution
///----------------
// The `waitTurn` method has a resolution of 10 milliseconds.  Therefore,
// `bslmt::Turnstile` cannot guarantee that all turns can be taken in each one
// second interval if a rate higher than 100 turns per second is specified.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Basic Usage
/// - - - - - - - - - - -
// The following example illustrates the use of `bslmt::Turnstile` to control
// the rate of output being written to a specified output stream.  The example
// function, `heartbeat`, prints a specified message at a specified rate for a
// specified duration.  An instance of `bsls::Stopwatch` is used to measure
// time against the specified duration.
// ```
// /// Write the specified `message` to the specified `stream` at the
// /// specified `rate` (given in messages per second) for the specified
// /// `duration`.
// static void heartbeat(bsl::ostream&       stream,
//                       const bsl::string&  message,
//                       double              rate,
//                       double              duration)
// {
//
//     bsls::Stopwatch  timer;
//     timer.start();
//     bslmt::Turnstile turnstile(rate);
//
//     while (true) {
//         turnstile.waitTurn();
//         if (timer.elapsedTime() >= duration) {
//             break;
//         }
//         stream << message;
//     }
// }
// ```
// The benefits of using `bslmt::Turnstile` in the above example, as opposed to
// simply calling `sleep` in a loop, are twofold.  Firstly, `bslmt::Turnstile`
// automatically accounts for drift caused by additional processing, so the
// loop is allowed to execute immediately if the program fails to execute the
// loop at the specified `rate`.  Secondly, computing the sleep time and
// executing the sleep call, are encapsulated in the turnstile component, which
// improves the overall readability of the program.

#include <bslscm_version.h>

#include <bsls_atomic.h>
#include <bsls_timeinterval.h>
#include <bsls_types.h>

namespace BloombergLP {
namespace bslmt {

                             // ===============
                             // class Turnstile
                             // ===============

/// This class provides a mechanism to meter time.  Using either the
/// constructor or the `reset` method, the client specifies `rate`,
/// indicating the number of events per second that the turnstile will
/// allow.  The client then calls `waitTurn`, which will either sleep until
/// the next event is to occur, or return immediately if `waitTurn` was
/// called after the next event is due.  If `waitTurn` is not called until
/// after the next event is due, the turnstile is said to be `lagging`
/// behind, and calls to `waitTurn` will not sleep until the events have
/// caught up with the schedule.  Note that calling `waitTurn` a single time
/// does not bring a turnstile back on schedule.  For example, if a
/// turnstile's configured frequency is one event per second, and the client
/// is 10 seconds behind schedule, if `waitTurn` were subsequently called
/// once per second, the turnstile will remain at 10 seconds behind
/// schedule.  The amount by which events are lagging behind the schedule
/// can be determined via the `lagTime` method, which returns the positive
/// number of microseconds by which the turnstile is lagging, or 0 if the
/// turnstile is not behind schedule.
class Turnstile {

    // DATA
    bsls::AtomicInt64         d_nextTurn;   // absolute time of next turn in
                                            // microseconds

    bsls::Types::Int64        d_interval;   // interval time in microseconds

    mutable bsls::AtomicInt64 d_timestamp;  // time of last call to 'now' in
                                            // microseconds

    int                       d_minTimeToCallSleep;
                                            // shortest period of time, in
                                            // microseconds, that the
                                            // 'waitTurn' function will go to
                                            // sleep

    // PRIVATE TYPES
    typedef bsls::Types::Int64 Int64;

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

  public:
    // CREATORS

    /// Create a turnstile object that admits clients at the specified
    /// `rate`, expressed as the number of turns per second.  Optionally
    /// specify the (relative) `startTime` of the first turn.  If
    /// `startTime` is not specified, the first turn may be taken
    /// immediately.  Optionally specify `minTimeToCallSleep`, the shortest
    /// period of time in seconds that the `waitTurn` function will go to
    /// sleep.  The behavior is undefined unless `0 < rate`.
    explicit
    Turnstile(double                    rate,
              const bsls::TimeInterval& startTime = bsls::TimeInterval(0),
              const bsls::TimeInterval& minTimeToCallSleep =
                                                     bsls::TimeInterval(1e-4));

    /// Destroy this object.
    //! ~Turnstile() = default;

    // MANIPULATORS

    /// Reset the rate of this turnstile to the specified `rate`, expressed
    /// as the number of turns per second.  Optionally specify the
    /// (relative) `startTime` of the first turn.  If `startTime` is not
    /// specified, the first turn may be taken immediately.  Optionally
    /// specify `minTimeToCallSleep`, the shortest period of time in
    /// seconds that the `waitTurn` function will go to sleep.  The behavior
    /// is undefined unless `0 < rate`.  Note that threads blocked on
    /// `waitTurn` are not interrupted.
    void reset(double                    rate,
               const bsls::TimeInterval& startTime = bsls::TimeInterval(0),
               const bsls::TimeInterval& minTimeToCallSleep =
                                                     bsls::TimeInterval(1e-4));

    /// Sleep until the next turn may be taken or return immediately if the
    /// optionally specified `sleep` is false, this turnstile is lagging
    /// behind schedule, or if the calculated sleep duration is less than
    /// `minTimeToCallSleep`.  Return the non-negative number of
    /// microseconds spent waiting, or if `sleep` is false, the number of
    /// microseconds the function would have had to wait.
    bsls::Types::Int64 waitTurn(bool sleep = true);

    // ACCESSORS

    /// Return the positive number of microseconds difference between the
    /// the current time and the next turn, indicating that the turnstile is
    /// lagging behind the configured rate.  Otherwise, return 0.
    bsls::Types::Int64 lagTime() const;
};

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