// bdlmt_throttle.h                                                   -*-C++-*-
#ifndef INCLUDED_BDLMT_THROTTLE
#define INCLUDED_BDLMT_THROTTLE

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

//@PURPOSE: Provide mechanism for limiting the rate at which actions may occur.
//
//@CLASSES:
//   bdlmt::Throttle: a mechanism for limiting the rate at which actions occur
//
//@MACROS:
//   BDLMT_THROTTLE_INIT, BDLMT_THROTTLE_INIT_REALTIME,
//   BDLMT_THROTTLE_INIT_ALLOW_ALL, BDLMT_THROTTLE_INIT_ALLOW_NONE,
//   BDLMT_THROTTLE_IF, BDLMT_THROTTLE_IF_REALTIME,
//   BDLMT_THROTTLE_IF_ALLOW_ALL, BDLMT_THROTTLE_IF_ALLOW_NONE
//
//@SEE_ALSO: bslmt_turnstile, btls_leakybucket
//
//@DESCRIPTION: This component provides a mechanism, 'bdlmt::Throttle', that
// can be used by clients to regulate the frequency at which actions can be
// taken.  Clients initialize a 'Throttle' with configuration values for
// 'nanosecondsPerAction', 'maxSimultaneousActions', and clock type.  Then
// clients request permission from this component to execute actions.  The
// component keeps track of the number of actions requested, and over time
// throttles the average number of actions permitted to a rate of
// '1 / nanosecondsPerAction' (actions-per-nanosecond).  So, for example, to
// limit the average rate of actions permitted to 10 actions per second
// (10 actions / one billion nanoseconds), the value for 'nanosecondsPerAction'
// would be 100000000 (which is one billion / 10).
//
// As clients request permission to perform actions the component accumulates a
// time debt for those actions that dissipates over time.  The maximum value
// for this time debt is given by
// 'maxSimultaneousActions * nanosecondsPerAction'.  The
// 'maxSimultaneousActions' configuration parameter thereby limits the maximum
// number of actions that can be simultaneously permitted.
//
// This behavior is known as a "leaky-bucket" algorithm: actions permitted
// place water in the bucket, the passage of time drains water from the bucket,
// and the bucket has a maximum capacity.  Actions are permitted when there is
// enough empty room in the bucket that the water placed won't overflow it.  A
// leaky bucket is an efficiently implementable approximation for allowing a
// certain number of actions over a window of time.
//
///Supported Clock-Types
///---------------------
// The component 'bsls::SystemClockType' supplies the enumeration indicating
// the system clock by which this component measures time.  By default, this
// component uses 'bsls::SystemClock::e_MONOTONIC'.  If the clock type
// indicated at initialization is 'bsls::SystemClockType::e_MONOTONIC', the
// timeout 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)'.  If the clock
// type indicated at initialization is 'bsls::SystemClockType::e_REALTIME', the
// time 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)'.
//
///Thread Safety
///-------------
// 'bdlmt::Throttle' is fully *thread-safe*, meaning that all
// non-initialization operations on a given instance instance can be safely
// invoked simultaneously from multiple threads.
//
///Static 'Throttle' Objects
///-------------------------
// 'Throttle' objects declared with static storage duration must be initialized
// using one of the 'BDLMT_THROTTLE_INIT*' macros.  In order to provide thread
// safety on C++03 compilers (which do not have 'constexpr'), these macros
// perform aggregate initialization that can be evaluated at compile time.
//
///Macro Reference
///---------------
//
///BLDMT_THROTTLE_INIT* macros
///- - - - - - - - - - - - - -
// One of these macros must be used to aggregate initialize 'bdlmt::Throttle'
// objects that have static storage duration -- the values are guaranteed to be
// evaluated at compile-time, avoiding race conditions.
//..
//   BDLMT_THROTTLE_INIT(maxSimultaneousActions,
//                       nanosecondsPerAction)
//   BDLMT_THROTTLE_INIT_REALTIME(maxSimultaneousActions,
//                                nanosecondsPerAction)
//       Initialize this 'Throttle' to limit the average period of actions
//       permitted to the specified 'nanosecondsPerAction', and the maximum
//       number of actions allowed at one time to the specified
//       'maxSimultaneousActions', where time is measured according to the
//       monotonic system clock.  These macros must be used for 'Throttle'
//       objects having static storage duration.  If 'maxSimultaneousActions'
//       is 0, the throttle will be configured to permit no actions.  If
//       'nanosecondsPerAction' is 0, the throttle will be configured to permit
//       all actions.  Use the '_REALTIME' variant of this macro to use the
//       real-time system clock to measure time, otherwise (by default) the
//       monotonic clock is used.  The behavior is undefined unless
//       '0 <= maxSimultaneousActions', '0 <= nanosecondsPerAction',
//       '0 < maxSimultaneousActions || 0 < nanosecondsPerAction', and
//       'maxSimultaneousActions * nanosecondsPerAction <= LLONG_MAX'.  Note
//       that floating-point expressions are not allowed in any of the
//       arguments, as they cannot be evaluated at compile-time on some
//       platforms.
//
//   BDLMT_THROTTLE_INIT_ALLOW_ALL
//       Initialize this 'Throttle' to allow all actions.
//
//   BDLMT_THROTTLE_INIT_ALLOW_NONE
//       Initialize this 'Throttle' to allow no actions.
//..
///BDLMT_THROTTLE_IF* macros
///- - - - - - - - - - - - -
//..
//   BDLMT_THROTTLE_IF(maxSimultaneousActions,
//                     nanosecondsPerAction)
//   BDLMT_THROTTLE_IF_REALTIME(maxSimultaneousActions,
//                              nanosecondsPerAction)
//       This macro behaves like an 'if' clause, executing the subsequent
//       statement or block if the time debt incurred by taking a single action
//       would *not* exceed the maximum allowed time debt indicated by the
//       specified 'nanosecondsPerAction' and 'maxSimultaneousActions'.  If
//       this 'if' clause is 'true' (and the subsequent statement or block is
//       executed), then 'nanosecondsPerAction' is added to the time debt
//       accumulated by this macro instantiation.  'nanosecondsPerAction' is
//       the minimum average period between actions permitted by this macro
//       instantiation, and 'nanosecondsPerAction' is the maximum number of
//       simultaneous actions permitted by this macro instantiation.  If
//       'maxSimultaneousActions' is 0, the 'if' clause will evaluate to
//       'false'.  If 'nanosecondsPerAction' is 0, the 'if' clause will
//       evaluate to 'true'.  Use the '_REALTIME' variant of this macro to use
//       the real-time system clock to measure time, otherwise (by default) the
//       monotonic clock is used.  The behavior is undefined unless
//       '0 <= maxSimultaneousActions', '0 <= nanosecondsPerAction', and
//       '0 < maxSimultaneousActions || 0 < nanosecondsPerAction'.  Note that
//       floating-point expressions are not allowed in any of the arguments, as
//       they cannot be evaluated at compile-time on some platforms.
//
//   BDLMT_THROTTLE_IF_ALLOW_ALL
//       Create an 'if' statement whose condition is always 'true', always
//       allowing execution of the statement controlled by it and never
//       allowing execution of any 'else' clause present.
//
//   BDLMT_THROTTLE_IF_ALLOW_NONE
//       Create an 'if' statement whose condition is always 'false', never
//       allowing execution of the statement controlled by it and always
//       allowing execution of any 'else' clause present.
//..
///Lack of 'bsl::chrono'-Based Overloads for 'requestPermission*'
///- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// 'bdlmt::Throttle' does not provide overloads for 'requestPermission' and
// 'requestPermissionIfValid' that take a 'bsl::chrono::time_point' as a
// representation for 'now'.  There are three reasons for this.  First,
// converting between different clocks is expensive, involving at least two
// calls to 'now' (one for the clock defined in the time point, and one for the
// clock used by the throttle).  This is supposed to be a performant component,
// allowing the caller to avoid the call to 'bsls::SystemTime::now' by passing
// in their own value of 'now'.  Second, it is inherently imprecise;
// conversions with the same input can return slightly different results,
// depending on the scheduling of the calls to 'now'.  Third, we have no way to
// support clocks that run at different rates.
//
///Usage
///-----
// In this section we show intended usage of this component.
//
///Example 1: Error Reporting
/// - - - - - - - - - - - - -
// Suppose we have an error reporting function 'reportError', that prints an
// error message to a log stream.  There is a possibility that 'reportError'
// will be called very frequently, and that reports of this error will
// overwhelm the other contents of the log, so we want to throttle the number
// of times this error will be reported.  For our application we decide that we
// want to see at most 10 reports of the error at any given time, and that if
// the error is occurring continuously, that we want a maximum sustained rate
// of one error report every five seconds.
//
// First, we declare the signature of our 'reportError' function:
//..
//  void reportError(bsl::ostream& stream)
//      // Report an error to the specified 'stream'.
//  {
//..
// Then, we define the maximum number of traces that can happen at a time to be
// 10:
//..
//      static const int maxSimultaneousTraces = 10;
//..
// Next, we define the minimum interval between subsequent reported errors, if
// errors are being continuously reported to be one report every 5 seconds.
// Note that the units are nanoseconds, which must be represented using a 64
// bit integral value:
//..
//      static const bsls::Types::Int64 nanosecondsPerSustainedTrace =
//                          5 * bdlt::TimeUnitRatio::k_NANOSECONDS_PER_SECOND;
//..
// Then, we declare our 'throttle' object and use the 'BDLMT_THROTTLE_INIT'
// macro to initialize it, using the two above constants.  Note that the two
// above constants *MUST* be calculated at compile-time, which means, among
// other things, that they can't contain any floating point sub-expressions:
//..
//      static bdlmt::Throttle throttle = BDLMT_THROTTLE_INIT(
//                        maxSimultaneousTraces, nanosecondsPerSustainedTrace);
//..
// Now, we call 'requestPermission' at run-time to determine whether to report
// the next error to the log:
//..
//      if (throttle.requestPermission()) {
//..
// Finally, we write the message to the log:
//..
//          stream << "Help!  I'm being held prisoner in a microprocessor!\n";
//      }
//  }
//..

#include <bdlscm_version.h>

#include <bdlt_timeunitratio.h>

#include <bslmf_assert.h>
#include <bslmf_integralconstant.h>

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

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

#include <bsl_climits.h>

namespace BloombergLP {
namespace bdlmt {

                               // ==============
                               // class Throttle
                               // ==============

class Throttle {
    // This 'class' provides a mechanism that can be used by clients to
    // regulate the frequency at which actions can be taken.  The data members
    // of 'Throttle' are currently public to allow for compile-time (aggregate)
    // initialization of 'Throttle' objects having static storage duration (for
    // C++03 compilers that do not provide 'constexpr').

    // PRIVATE TYPES
    typedef bsls::Types::Int64       Int64;
    typedef bsls::AtomicOperations   AtomicOps;
    typedef AtomicOps::AtomicTypes   AtomicTypes;

    // PRIVATE CONSTANTS
    enum { k_BILLION = 1000 * 1000 * 1000 };

    static const Int64 k_ALLOW_ALL   = LLONG_MIN;
    static const Int64 k_ALLOW_NONE  = LLONG_MAX;
    static const Int64 k_MAX_SECONDS = LLONG_MAX / k_BILLION;
    static const Int64 k_MIN_SECONDS = LLONG_MIN / k_BILLION;

  public:
    // PUBLIC CONSTANTS
    static const Int64 k_TEN_YEARS_NANOSECONDS = 10 * 366 *
                                    bdlt::TimeUnitRatio::k_NANOSECONDS_PER_DAY;

    // PUBLIC DATA
    AtomicTypes::Int64 d_prevLeakTime;                  // effective time of
                                                        // previous leak

    Int64              d_nanosecondsPerAction;          // nanoseconds per
                                                        // sustained action

    Int64              d_nanosecondsPerTotalReset;      // total bucket
                                                        // capacity in time

    int                d_maxSimultaneousActions;        // total bucket
                                                        // capacity in actions

    bsls::SystemClockType::Enum
                       d_clockType;                     // clock type --
                                                        // monotonic or
                                                        // realtime

  private:
    // FRIENDS
    template <int                t_MAX_SIMULTANEOUS_ACTIONS,
              bsls::Types::Int64 t_NANOSECONDS_PER_ACTION>
    friend class Throttle_InitHelper;

  public:
    // MANIPULATORS
    void initialize(int                         maxSimultaneousActions,
                    Int64                       nanosecondsPerAction,
                    bsls::SystemClockType::Enum clockType =
                                           bsls::SystemClockType::e_MONOTONIC);
        // Initialize this 'Throttle' to limit the average period of actions
        // permitted to the specified 'nanosecondsPerAction', and the maximum
        // number of simultaneous actions allowed to the specified
        // 'maxSimultaneousActions'.  Optionally specify 'clockType' to
        // indicate the system clock that will be used to measure time (see
        // {Supported Clock-Types} in the component documentation).  If
        // 'clockType' is not supplied the monotonic system clock is used.  The
        // configured throttle will over time limit the average number of
        // actions permitted to a rate of '1 / nanosecondsPerAction'.  If
        // 'maxSimultaneousActions' is 0, the throttle will be configured to
        // permit no actions, otherwise if 'nanosecondsPerAction' is 0, the
        // throttle will be configured to permit all actions.  The behavior is
        // undefined unless '0 <= nanosecondsPerAction',
        // '0 <= maxSimultaneousActions',
        // '0 < nanosecondsPerAction || 0 < maxSimultaneousActions', and
        // 'maxSimultaneousActions * nanosecondsPerActionLeak <= LLONG_MAX'.
        // Note that the behavior for other methods is undefined unless this
        // 'Throttle' is initialized (either using one of the overloads of this
        // function, or a 'BDLMT_THROTTLE_INIT' macro) prior to being called.

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
    void initialize(int                              maxSimultaneousActions,
                    Int64                            nanosecondsPerAction,
                    const bsl::chrono::system_clock&);
        // Initialize this 'Throttle' to limit the average period of actions
        // permitted to the specified 'nanosecondsPerAction', and the maximum
        // number of simultaneous actions allowed to the specified
        // 'maxSimultaneousActions'.  Use the realtime system clock to measure
        // time (see {Supported Clock-Types} in the component documentation).
        // The configured throttle will over time limit the average number of
        // actions permitted to a rate of '1 / nanosecondsPerAction'.  If
        // 'maxSimultaneousActions' is 0, the throttle will be configured to
        // permit no actions, otherwise if 'nanosecondsPerAction' is 0, the
        // throttle will be configured to permit all actions.  The behavior is
        // undefined unless '0 <= nanosecondsPerAction',
        // '0 <= maxSimultaneousActions',
        // '0 < nanosecondsPerAction || 0 < maxSimultaneousActions', and
        // 'maxSimultaneousActions * nanosecondsPerActionLeak <= LLONG_MAX'.
        // Note that the behavior for other methods is undefined unless this
        // 'Throttle' is initialized (either using one of the overloads of this
        // function, or a 'BDLMT_THROTTLE_INIT' macro) prior to being called.

    void initialize(int                              maxSimultaneousActions,
                    Int64                            nanosecondsPerAction,
                    const bsl::chrono::steady_clock&);
        // Initialize this 'Throttle' to limit the average period of actions
        // permitted to the specified 'nanosecondsPerAction', and the maximum
        // number of simultaneous actions allowed to the specified
        // 'maxSimultaneousActions'.  Use the monotonic system clock to measure
        // time (see {Supported Clock-Types} in the component documentation).
        // The configured throttle will over time limit the average number of
        // actions permitted to a rate of '1 / nanosecondsPerAction'.  If
        // 'maxSimultaneousActions' is 0, the throttle will be configured to
        // permit no actions, otherwise if 'nanosecondsPerAction' is 0, the
        // throttle will be configured to permit all actions.  The behavior is
        // undefined unless '0 <= nanosecondsPerAction',
        // '0 <= maxSimultaneousActions',
        // '0 < nanosecondsPerAction || 0 < maxSimultaneousActions', and
        // 'maxSimultaneousActions * nanosecondsPerActionLeak <= LLONG_MAX'.
        // Note that the behavior for other methods is undefined unless this
        // 'Throttle' is initialized (either using one of the overloads of this
        // function, or a 'BDLMT_THROTTLE_INIT' macro) prior to being called.
#endif

    bool requestPermission();
    bool requestPermission(const bsls::TimeInterval& now);
    bool requestPermission(int                       numActions);
    bool requestPermission(int                       numActions,
                           const bsls::TimeInterval& now);
        // Return 'true' if the time debt incurred by taking the indicated
        // action(s) would *not* exceed the maximum allowed time debt
        // configured for this 'Throttle' object
        // ('nanosecondsPerAction * maxSimultaneousActions'), and 'false'
        // otherwise.  Optionally specify 'now' indicating the current time of
        // the system clock for which this object is configured ('now' is a
        // offset from that clocks epoch).  If 'now' is not supplied, the
        // current time is obtained from the configured system clock.
        // Optionally specify 'numActions' indicating the number of actions
        // requested.  If 'numActions' is not supplied, one action is
        // requested.  If this function returns 'true' then
        // 'numActions * nanosecondsPerAction' is added to the time debt
        // accumulated by this component.  The behavior is undefined unless
        // this throttle has been initialized (either by calling an overload of
        // 'initialize' or using one of the 'BDLMT_THROTTLE_INIT*' macros),
        // '0 < numActions', ('numActions <= maxSimultaneousActions' or
        // '0 == maxSimultaneousActions'), and the value of 'now', if
        // specified, can be expressed in nanoseconds as a 64-bit signed
        // integer.  Note that 'requestPermissionIfValid', unlike these
        // methods, does not have any preconditions on the value of
        // 'numActions'.

    int requestPermissionIfValid(bool                      *result,
                                 int                        numActions);
    int requestPermissionIfValid(bool                      *result,
                                 int                        numActions,
                                 const bsls::TimeInterval&  now);
        // Set the specified '*result' to 'true' if the time debt incurred by
        // taking the specified 'numActions' would *not* exceed the maximum
        // allowed time debt configured for this 'Throttle' object
        // ('nanosecondsPerAction * maxSimultaneousActions'), and set '*result'
        // to 'false' otherwise.  Optionally specify 'now' indicating the
        // current time of the system clock for which this object is configured
        // ('now' is a offset from that clocks epoch).  If 'now' is not
        // supplied, the current time is obtained from the configured system
        // clock.  If '*result' is set to 'true' then
        // 'numActions * nanosecondsPerAction' is added to the time debt
        // accumulated by this component.  Return 0 if '0 <= numActions',
        // ('numActions <= maxSimultaneousActions' or
        // '0 == maxSimultaneousActions'), and the value of 'now', if
        // specified, can be expressed in nanoseconds as a 64-bit signed
        // integer, and a non-zero value otherwise.  The behavior is undefined
        // unless this throttle has been initialized (either by calling an
        // overload of 'initialize' or using one of the 'BDLMT_THROTTLE_INIT*'
        // macros).  Note that unless 0 is returned, '*result' is unaffected.

    // ACCESSOR
    bsls::SystemClockType::Enum clockType() const;
        // Return the system clock type with which this 'Throttle' is
        // configured to observe the passage of time.

    int maxSimultaneousActions() const;
        // Return the maximum number of simultaneous actions for which this
        // 'Throttle' is configured to permit.

    Int64 nanosecondsPerAction() const;
        // Return the time debt, in nanoseconds, that this 'Throttle' is
        // configured to incur for each action permitted.

    int nextPermit(bsls::TimeInterval *result, int numActions) const;
        // Load into the specified 'result' the earliest *absolute* *time*
        // (according to system clock configured at initialization) when the
        // specified 'numActions' will next be permitted.  Return 0 on success,
        // and a non-zero value (with no effect on 'result') if this throttle
        // is configured such that 'numActions' will never be permitted (i.e.,
        // return an error if 'numActions > maxSimultaneousActions') or if
        // 'numActions <= 0'.  The returned 'result' is an offset from the
        // epoch of the system clock for which this throttle is configured.
        // The behavior is undefined unless this throttle has been initialized
        // (either by calling 'initialize' or using a 'BDLMT_THROTTLE_INIT*'
        // macro).  Note that 'result' may be in the past, and this function
        // does *not* obtain the current time from the system clock.
};

                         // =========================
                         // class Throttle_InitHelper
                         // =========================

template <int                t_MAX_SIMULTANEOUS_ACTIONS,
          bsls::Types::Int64 t_NANOSECONDS_PER_ACTION>
class Throttle_InitHelper {
    // [!PRIVATE!] This component private meta-function is used to implement
    // the initialization macros.  This type provides the following:
    //: o Ensures arguments are evaluated at compile time (which won't be
    //:    the case for floating point arguments)
    //:
    //: o Enables compile time checks with BSLMF_ASSERT
    //:
    //: o Handles special cases if 0 is passed for
    //:   't_MAX_SIMULTANEOUS_ACTIONS' or t_NANOSECONDS_PER_ACTION'

    BSLMF_ASSERT(0 <= t_MAX_SIMULTANEOUS_ACTIONS);
    BSLMF_ASSERT(0 <= t_NANOSECONDS_PER_ACTION);
    BSLMF_ASSERT(t_MAX_SIMULTANEOUS_ACTIONS || t_NANOSECONDS_PER_ACTION);
    BSLMF_ASSERT(LLONG_MAX / (t_MAX_SIMULTANEOUS_ACTIONS
                                  ? t_MAX_SIMULTANEOUS_ACTIONS
                                  : 1) >=
                 t_NANOSECONDS_PER_ACTION);

  public:
    // PUBLIC CONSTANTS
    static const bsls::Types::Int64 k_npaValue =
                     0 == t_MAX_SIMULTANEOUS_ACTIONS ? Throttle::k_ALLOW_NONE
                     : t_NANOSECONDS_PER_ACTION      ? t_NANOSECONDS_PER_ACTION
                                                     : Throttle::k_ALLOW_ALL;

    static const int k_msaValue = 0 == t_NANOSECONDS_PER_ACTION
                                      ? INT_MAX
                                      : t_MAX_SIMULTANEOUS_ACTIONS;
};

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

                               // --------------
                               // class Throttle
                               // --------------

// MANIPULATORS
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY
inline
void Throttle::initialize(
                       int                              maxSimultaneousActions,
                       Int64                            nanosecondsPerAction,
                       const bsl::chrono::system_clock&)
{
    initialize(maxSimultaneousActions,
               nanosecondsPerAction,
               bsls::SystemClockType::e_REALTIME);
}

inline
void Throttle::initialize(
                       int                              maxSimultaneousActions,
                       Int64                            nanosecondsPerAction,
                       const bsl::chrono::steady_clock&)
{
    initialize(maxSimultaneousActions,
               nanosecondsPerAction,
               bsls::SystemClockType::e_MONOTONIC);
}
#endif

inline
bool Throttle::requestPermission()
{
    return this->requestPermission(bsls::SystemTime::now(d_clockType));
}

inline
bool Throttle::requestPermission(int numActions)
{
    return this->requestPermission(numActions,
                                   bsls::SystemTime::now(d_clockType));
}

inline
int Throttle::requestPermissionIfValid(bool                      *result,
                                       int                        numActions)
{
    if (numActions <= 0 || (d_maxSimultaneousActions < numActions &&
                                              0 != d_maxSimultaneousActions)) {
        return -1;                                                    // RETURN
    }

    *result = this->requestPermission(numActions,
                                      bsls::SystemTime::now(d_clockType));
    return 0;
}

// ACCESSORS
inline
bsls::SystemClockType::Enum Throttle::clockType() const
{
    return d_clockType;
}

inline
int Throttle::maxSimultaneousActions() const
{
    return d_maxSimultaneousActions;
}

inline
bsls::Types::Int64 Throttle::nanosecondsPerAction() const
{
    return d_nanosecondsPerAction;
}

                          // ---------------------------
                          // BDLMT_THROTTLE_INIT* macros
                          // ---------------------------

#define BDLMT_THROTTLE_INIT(maxSimultaneousActions,                           \
                            nanosecondsPerAction) {                           \
    { -BloombergLP::bdlmt::Throttle::k_TEN_YEARS_NANOSECONDS },               \
    BloombergLP::bdlmt::Throttle_InitHelper<                                  \
                                         (maxSimultaneousActions),            \
                                         (nanosecondsPerAction)>::k_npaValue, \
    bsl::integral_constant<BloombergLP::bsls::Types::Int64,                   \
         1LL * (maxSimultaneousActions) * (nanosecondsPerAction)>::value,     \
    BloombergLP::bdlmt::Throttle_InitHelper<                                  \
                                         (maxSimultaneousActions),            \
                                         (nanosecondsPerAction)>::k_msaValue, \
    BloombergLP::bsls::SystemClockType::e_MONOTONIC                           \
 }

#define BDLMT_THROTTLE_INIT_REALTIME(maxSimultaneousActions,                  \
                                     nanosecondsPerAction) {                  \
    { -BloombergLP::bdlmt::Throttle::k_TEN_YEARS_NANOSECONDS },               \
    BloombergLP::bdlmt::Throttle_InitHelper<                                  \
                                         (maxSimultaneousActions),            \
                                         (nanosecondsPerAction)>::k_npaValue, \
    bsl::integral_constant<BloombergLP::bsls::Types::Int64,                   \
         1LL * (maxSimultaneousActions) * (nanosecondsPerAction)>::value,     \
    BloombergLP::bdlmt::Throttle_InitHelper<                                  \
                                         (maxSimultaneousActions),            \
                                         (nanosecondsPerAction)>::k_msaValue, \
    BloombergLP::bsls::SystemClockType::e_REALTIME                            \
 }

#define BDLMT_THROTTLE_INIT_ALLOW_ALL  BDLMT_THROTTLE_INIT(1, 0)
#define BDLMT_THROTTLE_INIT_ALLOW_NONE BDLMT_THROTTLE_INIT(0, 1)

                        // ---------------------------
                        // 'BDLMT_THROTTLE_IF*' macros
                        // ---------------------------

#define BDLMT_THROTTLE_IF(maxSimultaneousActions,                             \
                          nanosecondsPerAction)                               \
    if (bool bdlmt_throttle_iFsToP = false) {}                                \
    else                                                                      \
        for (static BloombergLP::bdlmt::Throttle bdlmt_throttle_iFtHrOtTlE =  \
                                BDLMT_THROTTLE_INIT((maxSimultaneousActions), \
                                                    (nanosecondsPerAction));  \
             !bdlmt_throttle_iFsToP;                                          \
             bdlmt_throttle_iFsToP = true)                                    \
            if (bdlmt_throttle_iFtHrOtTlE.requestPermission())

#define BDLMT_THROTTLE_IF_REALTIME(maxSimultaneousActions,                    \
                                   nanosecondsPerAction)                      \
    if (bool bdlmt_throttle_iFsToP = false) {}                                \
    else                                                                      \
        for (static BloombergLP::bdlmt::Throttle bdlmt_throttle_iFtHrOtTlE =  \
                       BDLMT_THROTTLE_INIT_REALTIME((maxSimultaneousActions), \
                                                    (nanosecondsPerAction));  \
             !bdlmt_throttle_iFsToP;                                          \
             bdlmt_throttle_iFsToP = true)                                    \
            if (bdlmt_throttle_iFtHrOtTlE.requestPermission())

#define BDLMT_THROTTLE_IF_ALLOW_ALL    BDLMT_THROTTLE_IF(1, 0)
#define BDLMT_THROTTLE_IF_ALLOW_NONE   BDLMT_THROTTLE_IF(0, 1)

}  // close package namespace
}  // close enterprise namespace

#endif

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