// bdlmt_timereventscheduler.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_BDLMT_TIMEREVENTSCHEDULER
#define INCLUDED_BDLMT_TIMEREVENTSCHEDULER

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

//@PURPOSE: Provide a thread-safe recurring and non-recurring event scheduler.
//
//@CLASSES:
//  bdlmt::TimerEventScheduler: thread-safe event scheduler
//
//@SEE_ALSO: bdlmt_eventscheduler, bdlcc_timequeue
//
//@DESCRIPTION: This component provides a thread-safe event scheduler,
// 'bdlmt::TimerEventScheduler'.  It provides methods to schedule and cancel
// recurring and non-recurring events.  (A recurring event is also referred to
// as a clock).  The callbacks are processed by a separate thread (called the
// dispatcher thread).  By default the callbacks are executed in the dispatcher
// thread, but this behavior can be altered by providing a dispatcher functor
// at the creation time (see the section "The dispatcher thread and the
// dispatcher functor").  Use this component for implementing timeouts,
// deferred executions, calendars and reminders, and recurring tasks, among
// other time-bound behaviors.
//
// The number of active events permitted by the timer-event scheduler defaults
// to an implementation defined constant, and in any case no more than
// 2**24 - 1.  Note that if the scheduled event goes into infinite loop, and
// the default displatcher is used, the event scheduler may get into live lock.
//
///Comparison to 'bdlmt::EventScheduler'
///- - - - - - - - - - - - - - - - - - -
// This class has been made mostly obsolete by the newer
// 'bdlmt_eventscheduler', which addresses two main disadvantages of this
// component: 1) 'bdlmt_timereventscheduler' can only manage a finite number of
// events -- this limit is in the millions, but 'bdlmt_eventscheduler' has no
// such limit; and 2) accessing the queue of a 'bdlmt::TimerEventScheduler' is
// inefficient when there is a large number of managed events (since adding or
// removing an event involves a linear search); 'bdlmt_eventscheduler' has a
// more sophisticated queue that can be accessed in constant or worst-case
// log(n) time.  The advantage this component provides over
// 'bdlmt_eventscheduler' is that it provides light-weight handles to events in
// the queue, while 'bdlmt_eventscheduler' provides more heavy-weight
// reference-counted handles that must be released.
//
///Order of Execution of Events
///----------------------------
// It is intended that recurring and non-recurring events are processed as
// close as possible to their respective time values.  Delays and unfairness in
// thread contention can sometimes delay execution, but this component
// guarantees that (1) events are processed in increasing time order, and (2)
// are never processed sooner than their corresponding time (but could be
// processed arbitrarily long afterward, if the dispatcher thread has not been
// able to gain control in the meantime, due to thread contention or to a long
// event).
//
// Note that it is possible to schedule events in a scheduler that has not been
// started yet.  When starting a scheduler, scheduled events whose times have
// already passed will be dispatched as soon as possible after the start time,
// still in order of their corresponding time.
//
// The only exception to those guarantees are when an event 'e1' at time 'T'
// say, is already pending while another event 'e2' is scheduled at a time <=
// 'T'.  Then the dispatcher will complete the execution of 'e1' before
// dispatching 'e2'.
//
///The Dispatcher Thread and the Dispatcher Functor
///------------------------------------------------
// Between calls to 'start' and 'stop', the scheduler creates a separate thread
// (called the *dispatcher thread*) to process all the callbacks.  The
// dispatcher thread executes the callbacks by passing them to the dispatcher
// functor (optionally specified at creation time).  The default dispatcher
// functor simply invokes the passed callback, effectively executing it in the
// dispatcher thread.  Users can alter this behavior by defining their own
// dispatcher functor (for example in order to use a thread pool or a separate
// thread to run the callbacks).  In that case, the user-supplied functor will
// still be run in the dispatcher thread, different from the scheduler thread.
//
///Thread Safety
///-------------
// The 'bdlmt::TimerEventScheduler' class is both *fully thread-safe* (i.e.,
// all non-creator methods can correctly execute concurrently), and is
// *thread-enabled* (i.e., the classes does not function correctly in a
// non-multi-threading environment).  See 'bsldoc_glossary' for complete
// definitions of *fully thread-safe* and *thread-enabled*.
//
///Supported Clock-Types
///---------------------
// The component 'bsls::SystemClockType' supplies the enumeration indicating
// the system clock on which times supplied to other methods should be based.
// If the clock type indicated at construction is
// 'bsls::SystemClockType::e_REALTIME', time should be expressed as an absolute
// offset since 00:00:00 UTC, January 1, 1970 (which matches the epoch used in
// 'bdlt::CurrentTime::now(bsls::SystemClockType::e_REALTIME)'.  If the clock
// type indicated at construction is 'bsls::SystemClockType::e_MONOTONIC', time
// should be expressed as an absolute offset since the epoch of this clock
// (which matches the epoch used in
// 'bdlt::CurrentTime::now(bsls::SystemClockType::e_MONOTONIC)'.
//
// The current epoch time for a particular 'bdlmt::TimerEventScheduler'
// instance according to the correct clock is available via the
// 'bdlmt::TimerEventScheduler::now' accessor.
//
///Event Clock Substitution
///------------------------
// For testing purposes, a class 'bdlmt::TimerEventSchedulerTestTimeSource' is
// provided to allow manual manipulation of the system-time observed by a
// 'bdlmt::TimerEventScheduler'.  A test driver that interacts with a
// 'bdlmt::TimerEventScheduler' can use a
// 'bdlmt::TimerEventSchedulerTestTimeSource' object to control when scheduled
// events are triggered, allowing more reliable tests.
//
// A 'bdlmt::TimerEventSchedulerTestTimeSource' can be constructed for any
// existing 'bdlmt::TimerEventScheduler' object that has not been started and
// has not had any events scheduled.  When the
// 'bdlmt::TimerEventSchedulerTestTimeSource' is constructed, it will replace
// the clock of the 'bdlmt::TimerEventScheduler' to which it is attached.  The
// internal clock of the 'bdlmt::TimerEventSchedulerTestTimeSource' will be
// initialized with an arbitrary value on construction, and will advance only
// when explicitly instructed to do so by a call to
// 'bdlt::TimerEventSchedulerTestTimeSource::advanceTime'.  The current value
// of the internal clock can be accessed by calling
// 'bdlt::TimerEventSchedulerTestTimeSource::now', or
// 'bdlmt::TimerEventScheduler::now' on the instance supplied to the
// 'bdlmt::TimerEventSchedulerTestTimeSource'.
//
// Note that the initial value of
// 'bdlt::TimerEventSchedulerTestTimeSource::now' is intentionally not
// synchronized with 'bdlt::CurrentTime::now'.  All test events scheduled for a
// 'bdlmt::TimerEventScheduler' that is instrumented with a
// 'bdlt::TimerEventSchedulerTestTimeSource' should be scheduled in terms of an
// offset from whatever arbitrary time is reported by
// 'bdlt::TimerEventSchedulerTestTimeSource'.  See Example 3 below for an
// illustration of how this is done.
//
///Usage
///-----
// The following example shows how to use a 'bdlmt::TimerEventScheduler' to
// implement a timeout mechanism in a server.  'my_Session' maintains several
// connections.  It closes a connection if the data for it does not arrive
// before a timeout (specified at the server creation time).
//
//..
//    class my_Session{
//        // This class encapsulates the data and state associated with a
//        // connection and provides a method 'processData' to process the
//        // incoming data for the connection.
//      public:
//        int processData(void *data, int length);
//            // Process the specified 'data' of the specified 'length'.
//    };
//
//    class my_Server {
//     // This class implements a server maintaining several connections.
//     // A connection is closed if the data for it does not arrive
//     // before a timeout (specified at the server creation time).
//
//     struct Connection {
//         bdlmt::TimerEventScheduler::Handle d_timerId; // handle for timeout
//                                                     // event
//
//         my_Session *d_session_p;                    // session for this
//                                                     // connection
//     };
//
//     bsl::vector<Connection*>     d_connections; // maintained connections
//     bdlmt::TimerEventScheduler     d_scheduler;   // timeout event scheduler
//     bsls::TimeInterval            d_ioTimeout;   // time out
//
//     void newConnection(Connection *connection);
//         // Add the specified 'connection' to this server and schedule
//         // the timeout event that closes this connection if the data
//         // for this connection does not arrive before the timeout.
//
//     void closeConnection(Connection *connection);
//         // Close the specified 'connection' and remove it from this server.
//
//     void dataAvailable(Connection *connection, void *data, int length);
//         // Return if the specified 'connection' has already timed-out.
//         // If not, cancel the existing timeout event for the 'connection',
//         // process the specified 'data' of the specified 'length' and
//         // schedule a new timeout event that closes the 'connection' if
//         // the data does not arrive before the timeout.
//
//   public:
//     my_Server(const bsls::TimeInterval&  ioTimeout,
//               bslma::Allocator          *allocator = 0);
//         // Construct a 'my_Server' object with a timeout value of
//         // 'ioTimeout' seconds.  Optionally specify a 'allocator' used to
//         // supply memory.  If 'allocator' is 0, the currently installed
//         // default allocator is used.
//
//     ~my_Server();
//         // Perform the required clean-up and destroy this object.
// };
//
// my_Server::my_Server(const bsls::TimeInterval&  ioTimeout,
//                      bslma::Allocator          *allocator)
// : d_connections(allocator)
// , d_scheduler(allocator)
// , d_ioTimeout(ioTimeout)
// {
//      // logic to start monitoring the arriving connections or data
//
//      d_scheduler.start();
// }
//
// my_Server::~my_Server()
// {
//     // logic to clean up
//
//     d_scheduler.stop();
// }
//
// void my_Server::newConnection(my_Server::Connection *connection)
// {
//     // logic to add 'connection' to the 'd_connections'
//
//     // setup the timeout for data arrival
//     connection->d_timerId = d_scheduler.scheduleEvent(
//        d_scheduler.now() + d_ioTimeout,
//        bdlf::BindUtil::bind(&my_Server::closeConnection, this, connection));
// }
//
// void my_Server::closeConnection(my_Server::Connection *connection)
// {
//     // logic to close the 'connection' and remove it from 'd_ioTimeout'
// }
//
// void my_Server::dataAvailable(my_Server::Connection *connection,
//                               void                  *data,
//                               int                   length)
// {
//     // If connection has already timed out and closed, simply return.
//     if (d_scheduler.cancelEvent(connection->d_timerId)) {
//         return;                                                // RETURN
//     }
//
//     // process the data
//     connection->d_session_p->processData(data, length);
//
//     // setup the timeout for data arrival
//     connection->d_timerId = d_scheduler.scheduleEvent(
//        d_scheduler.now() + d_ioTimeout,
//        bdlf::BindUtil::bind(&my_Server::closeConnection, this, connection));
// }
//..
//
///Example 3: Using the Test Time Source
///- - - - - - - - - - - - - - - - - - -
// For testing purposes, the class 'bdlmt::TimerEventSchedulerTestTimeSource'
// is provided to allow a test to manipulate the system-time observed by a
// 'bdlmt::TimeEventScheduler' in order to control when events are triggered.
// After a scheduler is constructed, a
// 'bdlmt::TimerEventSchedulerTestTimeSource' object can be created atop the
// scheduler.  A test can then use the test time-source to advance the
// scheduler's observed system-time in order to dispatch events in a manner
// coordinated by the test.  Note that a
// 'bdlmt::TimerEventSchedulerTestTimeSource' *must* be created on an
// event-scheduler before any events are scheduled, or the event-scheduler is
// started.
//
// This example shows how the clock may be altered:
//
//..
// void myCallbackFunction() {
//     puts("Event triggered!");
// }
//
// void testCase() {
//     // Construct the scheduler
//     bdlmt::TimerEventScheduler scheduler;
//
//     // Construct the time-source.
//     // Install the time-source in the scheduler.
//     bdlmt::TimerEventSchedulerTestTimeSource timeSource(&scheduler);
//
//     // Retrieve the initial time held in the time-source.
//     bsls::TimeInterval initialAbsoluteTime = timeSource.now();
//
//     // Schedule a single-run event at a 35s offset.
//     scheduler.scheduleEvent(initialAbsoluteTime + 35,
//                             bsl::function<void()()>(&myCallbackFunction));
//
//     // Schedule a 30s recurring event.
//     scheduler.startClock(bsls::TimeInterval(30),
//                          bsl::function<void()()>(&myCallbackFunction));
//
//     // Start the dispatcher thread.
//     scheduler.start();
//
//     // Advance the time by 40 seconds so that each
//     // event will run once.
//     timeSource.advanceTime(bsls::TimeInterval(40));
//
//     // The line "Event triggered!" should now have
//     // been printed to the console twice.
//
//     scheduler.stop();
// }
//..
//
// Note that this feature should be used only for testing purposes, never in
// production code.

#include <bdlscm_version.h>

#include <bdlcc_objectcatalog.h>
#include <bdlcc_timequeue.h>

#include <bdlma_concurrentpool.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bslmt_condition.h>
#include <bslmt_mutex.h>
#include <bslmt_threadattributes.h>
#include <bslmt_threadutil.h>

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

#include <bsl_functional.h>
#include <bsl_memory.h>
#include <bsl_vector.h>

namespace BloombergLP {

namespace bdlmt {

struct TimerEventSchedulerDispatcher;
class  TimerEventSchedulerTestTimeSource_Data;

                         // =========================
                         // class TimerEventScheduler
                         // =========================

class TimerEventScheduler {
    // This class provides a thread-safe event scheduler.  'scheduleEvent'
    // schedules a non-recurring event, returning a handle of type
    // 'TimerEventScheduler::Handle', which can be used to cancel the scheduled
    // event by invoking 'cancelEvent'.  Similarly, 'startClock' schedules a
    // recurring event, returning a handle of type
    // 'TimerEventScheduler::Handle', which can be used to cancel the clock by
    // invoking 'cancelClock'.  'cancelAllEvents' cancels all the registered
    // events and 'cancelAllClocks' cancels all the registered clocks.  The
    // callbacks are processed by a separate thread (called dispatcher thread).
    // By default the callbacks are executed in the dispatcher thread, but this
    // behavior can be altered by providing a dispatcher functor at the
    // creation time (see the section "The dispatcher thread and the dispatcher
    // functor").  'start' must be invoked to start dispatching the callbacks.
    // 'stop' stops the dispatching of the callbacks without removing the
    // pending events.

  private:
    // PRIVATE TYPES
    struct ClockData {
        // This structure encapsulates the data associated with a clock.

        bsl::function<void()>     d_callback;          // associated callback

        bsls::TimeInterval        d_periodicInterval;  // associated periodic
                                                       // interval

        volatile bool             d_isCancelled;       // tracks if the
                                                       // associated clock has
                                                       // been cancelled

        int                       d_handle;            // handle for clock
                                                       // callback

        // TRAITS
        BSLMF_NESTED_TRAIT_DECLARATION(ClockData, bslma::UsesBslmaAllocator);

        // CREATORS
        ClockData(const bsl::function<void()>&  callback,
                  const bsls::TimeInterval&     interval,
                  bslma::Allocator             *basicAllocator = 0)
        : d_callback(bsl::allocator_arg_t(),
                     bsl::allocator<bsl::function<void()> >(basicAllocator),
                     callback)
        , d_periodicInterval(interval)
        , d_isCancelled(false)
        , d_handle(0)
        {
        }

        ClockData(const ClockData&  original,
                  bslma::Allocator *basicAllocator = 0)
        : d_callback(bsl::allocator_arg_t(),
                     bsl::allocator<bsl::function<void()> >(basicAllocator),
                     original.d_callback)
        , d_periodicInterval(original.d_periodicInterval)
        , d_isCancelled(original.d_isCancelled)
        , d_handle(original.d_handle)
        {
        }
    };

    typedef bsl::shared_ptr<ClockData>                   ClockDataPtr;
    typedef bdlcc::TimeQueue<ClockDataPtr>               ClockTimeQueue;
    typedef bdlcc::TimeQueueItem<bsl::function<void()> > EventItem;
    typedef bdlcc::TimeQueue<bsl::function<void()> >     EventTimeQueue;
    typedef bsl::function<bsls::TimeInterval()>          CurrentTimeFunctor;

  public:
    // TYPES
    typedef int Handle;
        // Defines a type alias for a handle that identifies a scheduled clock
        // or event.

    typedef bsl::function<void(const bsl::function<void()>&)> Dispatcher;
        // Defines a type alias for the dispatcher functor type.

    typedef bdlcc::TimeQueue<bsl::function<void()> >::Key     EventKey;
        // Defines a type alias for a user-supplied key for identifying events.

    // CONSTANTS
    enum {
        e_INVALID_HANDLE = -1  // value of an invalid event or clock handle
#ifndef BDE_OMIT_INTERNAL_DEPRECATED
      , BCEP_INVALID_HANDLE = e_INVALID_HANDLE
      , INVALID_HANDLE = e_INVALID_HANDLE
#endif // BDE_OMIT_INTERNAL_DEPRECATED
    };

  private:
    // DATA
    bslma::Allocator *d_allocator_p;        // memory allocator (held)

    CurrentTimeFunctor
                      d_currentTimeFunctor; // when called, returns the current
                                            // time the scheduler should use
                                            // for the event timeline

    bdlma::ConcurrentPool
                      d_clockDataAllocator; // pool for 'ClockData' objects

    EventTimeQueue    d_eventTimeQueue;     // time queue for non recurring
                                            // events

    ClockTimeQueue    d_clockTimeQueue;     // time queue for clock events

    bdlcc::ObjectCatalog<ClockDataPtr>
                      d_clocks;             // catalog of clocks

    bslmt::Mutex      d_dispatcherMutex;    // serialize starting/stopping
                                            // dispatcher thread

    bslmt::Mutex      d_mutex;              // mutex used to control access to
                                            // this timer event scheduler

    bslmt::Condition  d_condition;          // condition variable used to
                                            // control access to this timer
                                            // event scheduler

    Dispatcher        d_dispatcherFunctor;  // functor used to dispatch events

    bsls::AtomicInt64 d_dispatcherId;       // id of the dispatcher thread

    bslmt::ThreadUtil::Handle
                      d_dispatcherThread;   // handle of the dispatcher thread

    bsls::AtomicInt   d_running;            // indicates if the timer event
                                            // scheduler is running

    bsls::AtomicInt   d_iterations;         // dispatcher cycle iteration
                                            // number

    bsl::vector<EventItem>
                      d_pendingEventItems;  // array of pending event callbacks

    int               d_currentEventIndex;  // index (in the array
                                            // 'd_pendingEventItems') of the
                                            // current event callback being
                                            // processed by dispatcher thread

    bsls::AtomicInt   d_numEvents;          // the number of events currently
                                            // registered and/or pending
                                            // dispatch (current callback is
                                            // NOT counted)

    bsls::AtomicInt   d_numClocks;          // number of clocks currently
                                            // registered

    bsls::SystemClockType::Enum
                      d_clockType;          // clock type used

    // NOT IMPLEMENTED
    TimerEventScheduler(const TimerEventScheduler& original);
    TimerEventScheduler& operator=(const TimerEventScheduler& rhs);

    // FRIENDS
    friend struct TimerEventSchedulerDispatcher;
    friend class  TimerEventSchedulerTestTimeSource;

  private:
    // PRIVATE MANIPULATORS
    void yieldToDispatcher();
        // Repeatedly wake up dispatcher thread until it noticeably starts
        // running.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(TimerEventScheduler,
                                   bslma::UsesBslmaAllocator);

    // CREATORS
    explicit TimerEventScheduler(bslma::Allocator *basicAllocator = 0);
        // Construct an event scheduler using the default dispatcher functor
        // (see the "The dispatcher thread and the dispatcher functor" section
        // in component-level doc) and use the realtime clock epoch for all
        // time intervals (see {Supported Clock-Types} in the component
        // documentation).  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.  Note that the  maximal number of
        // scheduled non-recurring events and recurring events defaults to
        // an implementation defined constant.

    explicit TimerEventScheduler(
                              bsls::SystemClockType::Enum  clockType,
                              bslma::Allocator            *basicAllocator = 0);
        // Construct an event scheduler using the default dispatcher functor
        // (see the "The dispatcher thread and the dispatcher functor" section
        // in component-level doc) and use the specified 'clockType' to
        // indicate the epoch used for all time intervals (see {Supported
        // Clock-Types} in the component documentation).  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.  Note that the
        // maximal number of scheduled non-recurring events and recurring
        // events defaults to an implementation defined constant.

    explicit TimerEventScheduler(const Dispatcher&  dispatcherFunctor,
                                 bslma::Allocator  *basicAllocator = 0);
        // Construct an event scheduler using the specified 'dispatcherFunctor'
        // (see "The dispatcher thread and the dispatcher functor" section in
        // component-level doc) and use the realtime clock epoch for all time
        // intervals (see {Supported Clock-Types} in the component
        // documentation).  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.  Note that the  maximal number of
        // scheduled non-recurring events and recurring events defaults to
        // an implementation defined constant.

    explicit TimerEventScheduler(
                              const Dispatcher&            dispatcherFunctor,
                              bsls::SystemClockType::Enum  clockType,
                              bslma::Allocator            *basicAllocator = 0);
        // Construct an event scheduler using the specified 'dispatcherFunctor'
        // (see "The dispatcher thread and the dispatcher functor" section in
        // component-level doc) and use the specified 'clockType' to indicate
        // the epoch used for all time intervals (see {Supported Clock-Types}
        // in the component documentation).  Optionally specify a
        // 'basicAllocator' used to supply memory.  If 'basicAllocator' is 0,
        // the currently installed default allocator is used.  Note that the
        // maximal number of scheduled non-recurring events and recurring
        // events defaults to an implementation defined constant.

    TimerEventScheduler(int               numEvents,
                        int               numClocks,
                        bslma::Allocator *basicAllocator = 0);
        // Construct a timer event scheduler using the default dispatcher
        // functor (see the "The dispatcher thread and the dispatcher functor"
        // section in component level doc) that has the capability to
        // concurrently schedule *at* *least* the specified 'numEvents' and
        // 'numClocks' and use the realtime clock epoch for all time intervals
        // (see {Supported Clock-Types} in the component documentation).
        // Optionally specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.  The behavior is undefined unless '0 <= numEvents < 2**24' and
        // '0 <= numClocks < 2**24'.

    TimerEventScheduler(int                          numEvents,
                        int                          numClocks,
                        bsls::SystemClockType::Enum  clockType,
                        bslma::Allocator            *basicAllocator = 0);
        // Construct a timer event scheduler using the default dispatcher
        // functor (see the "The dispatcher thread and the dispatcher functor"
        // section in component level doc) that has the capability to
        // concurrently schedule *at* *least* the specified 'numEvents' and
        // 'numClocks' and use the specified 'clockType' to indicate the epoch
        // used for all time intervals (see {Supported Clock-Types} in the
        // component documentation).  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.  The behavior is undefined
        // unless '0 <= numEvents < 2**24' and '0 <= numClocks < 2**24'.

    TimerEventScheduler(int                numEvents,
                        int                numClocks,
                        const Dispatcher&  dispatcherFunctor,
                        bslma::Allocator  *basicAllocator = 0);
        // Construct a timer event scheduler using the specified
        // 'dispatcherFunctor' (see "The dispatcher thread and the dispatcher
        // functor" section in component level doc) that has the capability to
        // concurrently schedule *at* *least* the specified 'numEvents' and
        // 'numClocks' and use the realtime clock epoch for all time intervals
        // (see {Supported Clock-Types} in the component documentation).
        // Optionally specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.  The behavior is undefined unless '0 <= numEvents < 2**24' and
        // '0 <= numClocks < 2**24'.

    TimerEventScheduler(int                          numEvents,
                        int                          numClocks,
                        const Dispatcher&            dispatcherFunctor,
                        bsls::SystemClockType::Enum  clockType,
                        bslma::Allocator            *basicAllocator = 0);
        // Construct a timer event scheduler using the specified
        // 'dispatcherFunctor' (see "The dispatcher thread and the dispatcher
        // functor" section in component level doc) that has the capability to
        // concurrently schedule *at* *least* the specified 'numEvents' and
        // 'numClocks' and use the specified 'clockType' to indicate the epoch
        // used for all time intervals (see {Supported Clock-Types} in the
        // component documentation).  Optionally specify a 'basicAllocator'
        // used to supply memory.  If 'basicAllocator' is 0, the currently
        // installed default allocator is used.  The behavior is undefined
        // unless '0 <= numEvents < 2**24' and '0 <= numClocks < 2**24'.

    ~TimerEventScheduler();
        // Stop this scheduler, discard all the unprocessed events and destroy
        // this object.

    // MANIPULATORS
    int start();
        // Begin dispatching events on this scheduler using default attributes
        // for the dispatcher thread.  Return 0 on success, and a nonzero value
        // otherwise.  If another thread is currently executing 'stop', wait
        // until the dispatcher thread stops before starting a new one.  If
        // this scheduler has already started (and is not currently being
        // stopped by another thread) then this invocation has no effect and 0
        // is returned.  The behavior is undefined if this method is invoked in
        // the dispatcher thread (i.e., in a job executed by this scheduler).
        // Note that any event whose time has already passed is pending and
        // will be dispatched immediately.

    int start(const bslmt::ThreadAttributes& threadAttributes);
        // Begin dispatching events on this scheduler using the specified
        // 'threadAttributes' for the dispatcher thread (except that the
        // DETACHED attribute is ignored).  Return 0 on success, and a nonzero
        // value otherwise.  If another thread is currently executing 'stop',
        // wait until the dispatcher thread stops before starting a new one.
        // If this scheduler has already started (and is not currently being
        // stopped by another thread) then this invocation has no effect and 0
        // is returned.  The behavior is undefined if this method is invoked in
        // the dispatcher thread (i.e., in a job executed by this scheduler).
        // Note that any event whose time has already passed is pending and
        // will be dispatched immediately.

    void stop();
        // End the dispatching of events on this scheduler (but do not remove
        // any pending events), and wait for any (one) currently executing
        // event to complete.  If the scheduler is already stopped then this
        // method has no effect.  This scheduler can be restarted by invoking
        // 'start'.  The behavior is undefined if this method is invoked from
        // the dispatcher thread.

    Handle scheduleEvent(const bsls::TimeInterval&    time,
                         const bsl::function<void()>& callback,
                         const EventKey&              key = EventKey(0));
        // Schedule the specified 'callback' to be dispatched at the specified
        // 'time'.  On success, return a handle that can be used to cancel the
        // 'callback' (by invoking 'cancelEvent'), or return 'e_INVALID_HANDLE'
        // if scheduling this event would exceed the maximum number of
        // scheduled events for this object (see constructor).  Optionally
        // specify 'key' to uniquely identify the event.  The 'time' is an
        // absolute time represented as an interval from some epoch, which is
        // detemined by the clock indicated at construction (see {Supported
        // Clock-Types} in the component documentation).

    int rescheduleEvent(Handle                    handle,
                        const bsls::TimeInterval& newTime,
                        bool                      wait = false);
    int rescheduleEvent(Handle                    handle,
                        const EventKey&           key,
                        const bsls::TimeInterval& newTime,
                        bool                      wait = false);
        // Reschedule the event having the specified 'handle' at the specified
        // 'newTime'.  Optionally use the specified 'key' to uniquely identify
        // the event.  If the optionally specified 'wait' is true, then ensure
        // that the event having the 'handle' (if it is valid) is either
        // successfully rescheduled or dispatched before the call returns.
        // Return 0 on successful reschedule, and a non-zero value if the
        // 'handle' is invalid *or* if the event has already been dispatched
        // *or* if the event has not yet been dispatched but will soon be
        // dispatched.  If this method is being invoked from the dispatcher
        // thread then the 'wait' is ignored to avoid deadlock.  The 'newTime'
        // is an absolute time represented as an interval from some epoch,
        // which is detemined by the clock indicated at construction (see
        // {Supported Clock-Types} in the component documentation).

    int cancelEvent(Handle          handle,
                    bool            wait = false);
    int cancelEvent(Handle          handle,
                    const EventKey& key,
                    bool            wait = false);
        // Cancel the event having the specified 'handle'.  Optionally use the
        // specified 'key' to uniquely identify the event.  If the optionally
        // specified 'wait' is true, then ensure that the dispatcher thread has
        // resumed execution before returning.  Return 0 on successful
        // cancellation, and a non-zero value if the 'handle' is invalid *or*
        // if it is too late to cancel the event.  If this method is being
        // invoked from the dispatcher thread then the 'wait' is ignored to
        // avoid deadlock.

    void cancelAllEvents(bool wait = false);
        // Cancel all the events.  If the optionally specified 'wait' is true,
        // then ensure any event still in this scheduler is either cancelled or
        // has been dispatched before this call returns.  If this method is
        // being invoked from the dispatcher thread then the 'wait' is ignored
        // to avoid deadlock.

    Handle startClock(
               const bsls::TimeInterval&    interval,
               const bsl::function<void()>& callback,
               const bsls::TimeInterval&    startTime = bsls::TimeInterval(0));
        // Schedule a recurring event that invokes the specified 'callback' at
        // every specified 'interval', starting at the optionally specified
        // 'startTime'.  On success, return a handle that can be use to cancel
        // the clock (by invoking 'cancelClock'), or return 'e_INVALID_HANDLE'
        // if scheduling this event would exceed the maximum number of
        // scheduled events for this object (see constructor).  If no start
        // time is specified, it is assumed to be the 'interval' time from now.
        // The 'startTime' is an absolute time represented as an interval from
        // some epoch, which is detemined by the clock indicated at
        // construction (see {Supported Clock-Types} in the component
        // documentation).

    int cancelClock(Handle handle, bool wait = false);
        // Cancel the clock having the specified 'handle'.  If the optionally
        // specified 'wait' is true, then ensure that any scheduled event for
        // the clock having 'handle' is either cancelled or has been dispatched
        // before this call returns.  Return 0 on success, and a non-zero value
        // if the 'handle' is invalid.  If this method is being invoked from
        // the dispatcher thread, then the 'wait' is ignored to avoid deadlock.

    void cancelAllClocks(bool wait = false);
        // Cancel all clocks.  If the optionally specified 'wait' is true, then
        // ensure that any clock event still in this scheduler is either
        // cancelled or has been dispatched before this call returns.  If this
        // method is being invoked from the dispatcher thread, then the 'wait'
        // is ignored to avoid deadlock.

    // ACCESSORS
    bsls::SystemClockType::Enum clockType() const;
        // Return the value of the clock type that this object was created
        // with.

    bsls::TimeInterval now() const;
        // Return the current epoch time, 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
        // documentation).

    int numClocks() const;
        // Return a *snapshot* of the number of registered clocks with this
        // scheduler.

    int numEvents() const;
        // Return a *snapshot* of the number of pending events and events being
        // dispatched in this scheduler.
};

                  // =======================================
                  // class TimerEventSchedulerTestTimeSource
                  // =======================================

class TimerEventSchedulerTestTimeSource {
    // This class provides a means to change the clock that is used by a given
    // event-scheduler to determine when events should be triggered.
    // Constructing a 'TimerEventSchedulerTestTimeSource' alters the behavior
    // of the supplied event-scheduler.  After a test time-source is created,
    // the underlying scheduler will run events according to a discrete
    // timeline, whose successive values are determined by calls to
    // 'advanceTime' on the test time-source, and can be retrieved by calling
    // 'now' on that test time-source.  Note that the "system-time" held by a
    // test time-source *does* *not* correspond to the current system time.
    // Test writers must use caution when scheduling absolute-time events so
    // that they are scheduled relative to the test time-source's value for
    // 'now'.

  private:
    // DATA
    bsl::shared_ptr<TimerEventSchedulerTestTimeSource_Data>
                          d_data_p;       // shared pointer to the state whose
                                          // lifetime must be as long as
                                          // '*this' and '*d_scheduler_p'

    TimerEventScheduler  *d_scheduler_p;  // pointer to the scheduler that we
                                          // are augmenting

  public:
    // CREATORS
    TimerEventSchedulerTestTimeSource(TimerEventScheduler *scheduler);
        // Construct a test time-source object that will control the
        // "system-time" observed by the specified 'scheduler'.  Initialize
        // 'now' to be an arbitrary time value.  The behavior is undefined if
        // any methods have previously been called on 'scheduler'.

    // MANIPULATORS
    bsls::TimeInterval advanceTime(bsls::TimeInterval amount);
        // Advance this object's current-time value by the specified 'amount'
        // of time, and notify the scheduler that the time has changed.  Return
        // the updated current-time value.  The behavior is undefined unless
        // 'amount' represents a positive time interval, and 'now + amount' is
        // within the range that can be represented with a
        // 'bsls::TimeInterval'.

    // ACCESSORS
    bsls::TimeInterval now();
        // Return this object's current-time value.  Upon construction, this
        // method will return an arbitrary value.  Subsequent calls to
        // 'advanceTime' will adjust the arbitrary value forward.
};

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

                            // -------------------
                            // TimerEventScheduler
                            // -------------------

// MANIPULATORS
inline
int TimerEventScheduler::cancelEvent(TimerEventScheduler::Handle handle,
                                     bool                        wait)
{
    return cancelEvent(handle, EventKey(0), wait);
}

inline
int TimerEventScheduler::rescheduleEvent(TimerEventScheduler::Handle handle,
                                         const bsls::TimeInterval&   newTime,
                                         bool                        wait)
{
    return rescheduleEvent(handle, EventKey(0), newTime, wait);
}

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

inline
bsls::TimeInterval TimerEventScheduler::now() const
{
    return d_currentTimeFunctor();
}

inline
int TimerEventScheduler::numClocks() const
{
    return d_numClocks;
}

inline
int TimerEventScheduler::numEvents() const
{
    return d_numEvents;
}

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