// bdlmt_eventscheduler.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_EVENTSCHEDULER #define INCLUDED_BDLMT_EVENTSCHEDULER #include <bsls_ident.h> BSLS_IDENT("$Id: $") //@PURPOSE: Provide a thread-safe recurring and one-time event scheduler. // //@CLASSES: // bdlmt::EventScheduler: a thread-safe event scheduler // bdlmt::EventSchedulerEventHandle: handle to a single scheduled event // bdlmt::EventSchedulerRecurringEventHandle: handle to a recurring event // bdlmt::EventSchedulerTestTimeSource: class for testing time changes // //@SEE_ALSO: bdlmt_timereventscheduler // //@DESCRIPTION: This component provides a thread-safe event scheduler. // 'bdlmt::EventScheduler', that implements methods to schedule and cancel // recurring and one-time events. All of the callbacks for these events are // processed by a separate thread (called the dispatcher thread). By default // the callbacks are also executed in the dispatcher thread, but that behavior // can be altered by providing a dispatcher functor at creation time (see the // section {The Dispatcher Thread and the Dispatcher Functor}). // // Events may be referred to by 'bdlmt::EventSchedulerEventHandle' and // 'bdlmt::EventSchedulerRecurringEventHandle' objects, which clean up after // themselves when they go out of scope, or by 'Event' and 'RecurringEvent' // pointers, which must be released using 'releaseEventRaw'. Such pointers are // used in the "Raw" API of this class and must be used carefully. Note that // the Handle objects have an implicit conversion to the corresponding 'Event' // or 'RecurringEvent' pointer types, effectively providing extra overloads for // methods that take a 'const Event*' to also take a 'const EventHandle&'. // ///Comparison to 'bdlmt::TimerEventScheduler' /// - - - - - - - - - - - - - - - - - - - - - // This component was written after 'bdlmt_timereventscheduler', which suffered // from a couple of short-comings: 1) there was a maximum number of events it // could manage, and 2) it was inefficient at dealing with large numbers of // events. This component addresses both those problems -- there is no limit // on the number of events it can manage, and it is more efficient at dealing // with large numbers of events. The disadvantage of this component relative // to 'bdlmt_timereventscheduler' is that handles referring to managed events // in a 'bdlmt::EventScheduler' are reference-counted and need to be released, // while handles of events in a 'bdlmt::TimerEventScheduler' are integral types // that do not need to be released. // ///Thread Safety and "Raw" Event Pointers ///-------------------------------------- // 'bdlmt::EventScheduler' is thread-safe and thread-enabled, meaning that // multiple threads may use their own instances of the class or use a shared // instance without further synchronization. The thread safety and correct // behavior of the component depend on the correct usage of 'Event' pointers, // which refer to scheduled events in the "Raw" API of this class. In // particular: //.. // * Every 'Event*' and 'RecurringEvent*' populated by 'scheduleEventRaw' // and 'scheduleRecurringEventRaw' must be released using 'releaseEventRaw.' // - Pointers are not released automatically when events are completed. // - Pointers are not released automatically when events are canceled. // - Events are not canceled when pointers to them are released. // * Pointers must not be used after being released. // * Pointers must never be shared or duplicated without using // 'addEventRefRaw' and 'addRecurringEventRefRaw' to get additional // references; *each* such added reference must be released separately. //.. // 'bdlmt::EventSchedulerEventHandle' and // 'bdlmt::EventSchedulerRecurringEventHandle' are *const* *thread-safe*. It // is not safe for multiple threads to invoke non-'const' methods on the same // 'EventHandle' or 'RecurringEventHandle' object concurrently. // ///The Dispatcher Thread and the Dispatcher Functor ///------------------------------------------------ // The scheduler creates a single 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). Note // that the user-supplied functor will still be run in the dispatcher thread. // // CAVEAT: Using a dispatcher functor such as the example above (to execute the // callback in a separate thread) violates the guarantees of // cancelEventAndWait(). Users who specify a dispatcher functor that transfers // the event to another thread for execution should not use // cancelEventAndWait(), and should instead ensure that the lifetime of any // object bound to an event exceeds the lifetime of the mechanism used by the // customized dispatcher functor. // ///Timer Resolution and Order of Execution ///--------------------------------------- // It is intended that recurring and one-time events are processed as closely // as possible to their respective time values, and that they are processed in // the order scheduled. However, this component *guarantees* only that events // will not be executed before their scheduled time. Generally, events that // are scheduled more than 1 microsecond apart will be executed in the order // scheduled; but different behavior may be observed when events are submitted // after (or shortly before) their scheduled time. // // When events are executed in the dispatcher thread and take longer to // complete than the time between events, the dispatcher can fall behind. In // this case, events will be executed in the correct order as soon as the // dispatcher thread becomes available; once the backlog is worked off, events // will be executed at or near their scheduled times. // ///Supported Clock Types ///--------------------- // An 'EventScheduler' optionally accepts a clock type at construction // indicating the clock by which it will internally schedule events. The clock // type may be indicated by either a 'bsls::SystemClockType::Enum' value, a // 'bsl::chrono::system_clock' object (which is equivalent to specifying // 'e_REALTIME'), or a 'bsl::chrono::steady_clock' object (equivalent to // specifying 'e_MONOTONIC'). If a clock type is not specified, 'e_REALTIME' // is used. // ///Scheduling Using a 'bsl::chrono::time_point' /// - - - - - - - - - - - - - - - - - - - - - - // When creating either a one-time or recurring event, clients may pass a // 'bsl::chrono::time_point' indicating the time the event should occur. This // 'time_point' object can be associated with an arbitrary clock. If the // 'time_point' is associated with a different clock than was indicated at // construction of the event scheduler, those time points are converted to be // relative to the event scheduler's clock for processing. A possible // implementation of such a conversion would be: //.. // bsls::TimeInterval(time - t_CLOCK::now()) + eventScheduler.now() //.. // where 'time' is a 'time_point', 't_CLOCK' is the clock associated with // 'time', and 'eventScheduler' is the 'EventScheduler' on which the event is // being scheduled. Notice that the conversion adds some imprecision and // overhead to evaluation of the event. An event scheduler guarantees an event // will occur at or after the supplied 'time_point', even if that 'time_point' // is defined in terms of a 't_CLOCK' different from the one used by the event // scheduler. If there is a discontinuity between the clock for a 'time_point' // and the event scheduler's clock, additional processing overhead may result // (because the event may need to be rescheduled), and the event may also occur // later than what one might otherwise expect. // ///Scheduling Using a 'bsls::TimeInterval' ///- - - - - - - - - - - - - - - - - - - - // When creating either a one-time or recurring event, clients may pass a // 'bsls::TimeInterval' indicating the time the event should occur as an offset // from an epoch. 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)', and // 'bsl::chrono::system_clock::now()'). 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)' and // 'bsl::chrono::steady_clock'). // ///Event Clock Substitution ///------------------------ // For testing purposes, a class 'bdlmt::EventSchedulerTestTimeSource' is // provided to allow manual manipulation of the system-time observed by a // 'bdlmt::EventScheduler'. A test driver that interacts with a // 'bdlmt::EventScheduler' can use a 'bdlmt::EventSchedulerTestTimeSource' // object to control when scheduled events are triggered, allowing more // reliable tests. // // A 'bdlmt::EventSchedulerTestTimeSource' can be constructed for any existing // 'bdlmt::EventScheduler' object that has not been started and has not had any // events scheduled. When the 'bdlmt::EventSchedulerTestTimeSource' is // constructed, it will replace the clock of the 'bdlmt::EventScheduler' to // which it is attached. The internal clock of the // 'bdlmt::EventSchedulerTestTimeSource' will be initialized with an arbitrary // value on construction, and will advance only when explicitly instructed to // do so by a call to 'bdlt::EventSchedulerTestTimeSource::advanceTime'. The // current value of the internal clock can be accessed by calling // 'bdlt::EventSchedulerTestTimeSource::now', or 'bdlmt::EventScheduler::now' // on the instance supplied to the 'bdlmt::EventSchedulerTestTimeSource'. // // Note that the initial value of 'bdlt::EventSchedulerTestTimeSource::now' is // intentionally not synchronized with 'bsls::SystemTime::nowRealtimeClock'. // All test events scheduled for a 'bdlmt::EventScheduler' that is instrumented // with a 'bdlt::EventSchedulerTestTimeSource' should be scheduled in terms of // an offset from whatever arbitrary time is reported by // 'bdlt::EventSchedulerTestTimeSource'. See Example 3 below for an // illustration of how this is done. // ///Thread Name for Dispatcher Thread ///--------------------------------- // To facilitate debugging, users can provide a thread name as the 'threadName' // attribute of the 'bslmt::ThreadAttributes' argument passed to the 'start' // method, that will be used for the dispatcher thread. The thread name should // not be used programmatically, but will appear in debugging tools on // platforms that support naming threads to help users identify the source and // purpose of a thread. If no 'ThreadAttributes' object is passed, or if the // 'threadName' attribute is not set, the default value "bdl.EventSched" will // be used. // ///Usage ///----- // This section illustrates intended use of this component. // ///Example 1: Simple Clock ///- - - - - - - - - - - - // In this example we wish to log some statistics periodically. We define a // method to store the value of a variable into an array, and set up a // scheduler to call that as a recurring event. //.. // bsls::AtomicInt g_data; // Some global data we want to track // typedef pair<bsls::TimeInterval, int> Value; // // void saveData(vector<Value> *array) // { // array->push_back(Value(bsls::SystemTime::nowRealtimeClock(), g_data)); // } //.. // We allow the scheduler to run for a short time while changing this value and // observe that the callback is executed: //.. // bdlmt::EventScheduler scheduler; // vector<Value> values; // // scheduler.scheduleRecurringEvent(bsls::TimeInterval(1.5), // bdlf::BindUtil::bind(&saveData, &values))); // scheduler.start(); // bsls::TimeInterval start = bsls::SystemTime::nowRealtimeClock(); // while ((bsls::SystemTime::nowRealtimeClock() - // start).totalSecondsAsDouble() < 7) { // ++g_data; // } // scheduler.stop(); // assert(values.size() >= 4); // for (int i = 0; i < (int) values.size(); ++i) { // cout << "At " << bdlt::EpochUtil::convertFromTimeInterval( // values[i].first) << // " g_data was " << values[i].second << endl; // } //.. // This will display, e.g.: //.. // At 26OCT2020_23:51:51.097283 g_data was 8008406 // At 26OCT2020_23:51:52.597287 g_data was 16723918 // At 26OCT2020_23:51:54.097269 g_data was 24563722 // At 26OCT2020_23:51:55.597262 g_data was 30291748 //.. // ///Example 2: Server Timeouts /// - - - - - - - - - - - - - // The following example shows how to use a 'bdlmt::EventScheduler' 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). // // Because the timeout is relative to the arrival of data, it is best to use a // "monotonic" clock that advances at a steady rate, rather than a "wall" clock // that may fluctuate to reflect real time adjustments. //.. // 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'. (TBD) // }; // // 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::EventSchedulerEventHandle d_timerId; // handle for timeout // // event // // my_Session *d_session_p; // session for this // // connection // }; // // bsl::vector<Connection*> d_connections; // maintained connections // bdlmt::EventScheduler 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); // // Create 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 *alloc) // : d_connections(alloc) // , d_scheduler(bsls::SystemClockType::e_MONOTONIC, alloc) // , d_ioTimeout(ioTimeout) // { // // TBD: logic to start monitoring the arriving connections or data // // d_scheduler.start(); // } // // my_Server::~my_Server() // { // // TBD: logic to clean up // // d_scheduler.stop(); // } // // void my_Server::newConnection(my_Server::Connection *connection) // { // // TBD: logic to add 'connection' to 'd_connections' // // // setup the timeout for data arrival // d_scheduler.scheduleEvent( // &connection->d_timerId, // d_scheduler.now() + d_ioTimeout, // bdlf::BindUtil::bind(&my_Server::closeConnection, this, connection)); // } // // void my_Server::closeConnection(my_Server::Connection *connection) // { // // TBD: 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 // d_scheduler.scheduleEvent( // &connection->d_timerId, // 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::EventSchedulerTestTimeSource' is // provided to allow a test to manipulate the system-time observed by a // 'bdlmt::EventScheduler' in order to control when events are triggered. // After a scheduler is constructed, a 'bdlmt::EventSchedulerTestTimeSource' // 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::EventSchedulerTestTimeSource' *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() { // // Create the scheduler // bdlmt::EventScheduler scheduler; // // // Create the time-source. // // Install the time-source in the scheduler. // bdlmt::EventSchedulerTestTimeSource 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.scheduleRecurringEvent(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_skiplist.h> #include <bdlf_bind.h> #include <bdlf_placeholder.h> #include <bslma_allocator.h> #include <bslma_usesbslmaallocator.h> #include <bslmf_allocatorargt.h> #include <bslmf_nestedtraitdeclaration.h> #include <bslmt_condition.h> #include <bslmt_lockguard.h> #include <bslmt_mutex.h> #include <bslmt_threadattributes.h> #include <bslmt_threadutil.h> #include <bsls_assert.h> #include <bsls_atomic.h> #include <bsls_libraryfeatures.h> #include <bsls_review.h> #include <bsls_systemclocktype.h> #include <bsls_timeinterval.h> #include <bsls_types.h> #include <bsl_functional.h> #include <bsl_memory.h> #include <bsl_utility.h> #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY #include <bslmt_chronoutil.h> #include <bsl_chrono.h> #endif namespace BloombergLP { namespace bdlmt { class EventSchedulerEventHandle; class EventSchedulerRecurringEventHandle; class EventSchedulerTestTimeSource_Data; // ==================== // class EventScheduler // ==================== class EventScheduler { // This class provides a thread-safe event scheduler that executes // callbacks in a separate "dispatcher thread." 'start' must be invoked to // start dispatching the callbacks. 'stop' pauses the dispatching of the // callbacks without removing the pending events. private: // PRIVATE TYPES // ================ // struct EventData // ================ struct EventData { // This 'struct' encapsulates all of the information for a // non-recurring event. public: // DATA bsl::function<void()> d_callback; // user-supplied callback invoked when associated event triggers bsl::function<bsls::Types::Int64()> d_nowOffset; // a function that returns the difference, in microseconds, between // when the scheduled event is meant to occur and the current time private: // NOT IMPLEMENTED EventData& operator=(const EventData&); public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(EventData, bslma::UsesBslmaAllocator); // CREATORS EventData( const bsl::function<void()>& callback, const bsl::function<bsls::Types::Int64()>& nowOffset, bslma::Allocator *basicAllocator = 0) // Create an 'EventData' from the specified 'callback' and // 'nowOffset'. Optionally specify a 'basicAllocator' used to // supply memory. If 'basicAllocator' is 0, the currently // installed default allocator is used. : d_callback(bsl::allocator_arg, basicAllocator, callback) , d_nowOffset(bsl::allocator_arg, basicAllocator, nowOffset) { } EventData(const EventData& original, bslma::Allocator *basicAllocator = 0) // Create an 'EventData' object having the value of the specified // 'original' object. Optionally specify a 'basicAllocator' used // to supply memory. If 'basicAllocator' is 0, the currently // installed default allocator is used. : d_callback(bsl::allocator_arg, basicAllocator, original.d_callback) , d_nowOffset(bsl::allocator_arg, basicAllocator, original.d_nowOffset) { } }; // ========================= // struct RecurringEventData // ========================= struct RecurringEventData { // This 'struct' encapsulates all of the information for a recurring // event. public: // DATA bsls::TimeInterval d_interval; // the time between calls (in microseconds) bsl::function<void()> d_callback; // user-supplied callback invoked when associated event triggers bsl::function<bsls::Types::Int64(int)> d_nowOffset; // a function that returns the difference, in microseconds, between // when the scheduled event is meant to occur and the current time int d_eventIdx; // the index of the recurring event (starting with 0); passed to // 'd_nowOffset' to determine the time of the next invocation of // 'd_callback' private: // NOT IMPLEMENTED RecurringEventData& operator=(const RecurringEventData&); public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(RecurringEventData, bslma::UsesBslmaAllocator); // CREATORS RecurringEventData( const bsls::TimeInterval& interval, const bsl::function<void()>& callback, const bsl::function<bsls::Types::Int64(int)>& nowOffset, bslma::Allocator *basicAllocator = 0) // Create a 'RecurringEventData' from the specified 'interval', // 'callback', and 'nowOffset'. Optionally specify a // 'basicAllocator' used to supply memory. If 'basicAllocator' is // 0, the currently installed default allocator is used. : d_interval(interval) , d_callback(bsl::allocator_arg, basicAllocator, callback) , d_nowOffset(bsl::allocator_arg, basicAllocator, nowOffset) , d_eventIdx(0) { } RecurringEventData(const RecurringEventData& original, bslma::Allocator *basicAllocator = 0) // Create a 'RecurringEventData' object having the value of the // specified 'original' object. Optionally specify a // 'basicAllocator' used to supply memory. If 'basicAllocator' is // 0, the currently installed default allocator is used. : d_interval(original.d_interval) , d_callback(bsl::allocator_arg, basicAllocator, original.d_callback) , d_nowOffset(bsl::allocator_arg, basicAllocator, original.d_nowOffset) , d_eventIdx(original.d_eventIdx) { } }; typedef bdlcc::SkipList<bsls::Types::Int64, RecurringEventData> RecurringEventQueue; typedef bdlcc::SkipList<bsls::Types::Int64, EventData> EventQueue; typedef bsl::function<bsls::TimeInterval()> CurrentTimeFunctor; // FRIENDS friend class EventSchedulerEventHandle; friend class EventSchedulerRecurringEventHandle; friend class EventSchedulerTestTimeSource; public: // PUBLIC TYPES struct Event {}; struct RecurringEvent {}; // Pointers to the opaque structures 'Event' and 'RecurringEvent' are // populated by the "Raw" API of 'EventScheduler'. typedef EventSchedulerEventHandle EventHandle; typedef EventSchedulerRecurringEventHandle RecurringEventHandle; typedef bsl::function<void(const bsl::function<void()>&)> Dispatcher; // Defines a type alias for the dispatcher functor type. private: // NOT IMPLEMENTED EventScheduler(const EventScheduler&); EventScheduler& operator=(const EventScheduler&); private: // PRIVATE CLASS DATA static const char s_defaultThreadName[16]; // Thread name to use when // none is specified. // PRIVATE DATA CurrentTimeFunctor d_currentTimeFunctor; // when called, returns the // current time the scheduler // should use for the event // timeline EventQueue d_eventQueue; // events RecurringEventQueue d_recurringQueue; // recurring events Dispatcher d_dispatcherFunctor; // dispatch events bslmt::ThreadUtil::Handle d_dispatcherThread; // dispatcher thread handle bsls::AtomicUint64 d_dispatcherThreadId; // dispatcher thread id used to // implement function // 'isInDispatcherThread' bslmt::Mutex d_dispatcherMutex; // serialize starting/stopping // dispatcher thread mutable bslmt::Mutex d_mutex; // synchronizes access to // condition variables bslmt::Condition d_queueCondition; // condition variable used to // signal when the queues need // to be checked again (when // they become non-empty or get // a new front member) bslmt::Condition d_iterationCondition; // condition variable used to // signal when the dispatcher // is ready to enter next // iteration (synchronizes // 'wait' methods) bool d_running; // controls the looping of the // dispatcher thread bool d_dispatcherAwaited; // A thread is waiting for the // dispatcher to complete an // iteration RecurringEventQueue::Pair *d_currentRecurringEvent; // Raw reference to the // scheduled event being // executed EventQueue::Pair *d_currentEvent; // Raw reference to the // scheduled recurring event // being executed unsigned int d_waitCount; // count of the number of waits // performed in the main // dispatch loop, used in // 'advanceTime' to determine // when to return bsls::SystemClockType::Enum d_clockType; // clock type used // PRIVATE CLASS METHODS static bsls::Types::Int64 returnZero(); // Return 0. static bsls::Types::Int64 returnZeroInt(int); // Return 0. The 'int' argument is ignored. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class t_CLOCK, class t_DURATION> static bsls::Types::Int64 timeUntilTrigger( const bsl::chrono::time_point<t_CLOCK, t_DURATION>& absTime); // Return the number of microseconds between the current time and the // specified 'absTime'. 'absTime' is an *absolute* time represented as // an interval from some epoch, which is determined by the clock // associated with the time point. Note that this method is used when // the 't_CLOCK' type used to schedule the event differs from that of // the event scheduler itself. Also note that a negative value is // returned if 'absTime' is in the past. template <class t_CLOCK, class t_DURATION, class t_REP_TYPE, class t_PERIOD_TYPE> static bsls::Types::Int64 timeUntilTriggerRecurring( const bsl::chrono::time_point<t_CLOCK, t_DURATION>& absTime, const bsl::chrono::duration<t_REP_TYPE, t_PERIOD_TYPE>& interval, int eventIndex); // Return the number of microseconds between the current time and the // scheduled time of the specified 'eventIndex'th recurring event, // which starts at the specified 'absTime' and repeats at the specified // 'interval'. 'absTime' is an *absolute* time represented as an // interval from some epoch, which is determined by the clock // associated with the time point. The behavior is undefined unless // '0 <= eventIndex'. Note that this method is used when the 't_CLOCK' // type used to schedule the event differs from that of the event // scheduler itself. #endif // PRIVATE MANIPULATORS bsls::Types::Int64 chooseNextEvent(bsls::Types::Int64 *now); // Pick either 'd_currentEvent' or 'd_currentRecurringEvent' as the // next event to be executed, given that the current time is the // specified (absolute) 'now' interval, and return the (absolute) // interval of the chosen event. If both 'd_currentEvent' and // 'd_currentRecurringEvent' are valid, release whichever one was not // chosen. If both 'd_currentEvent' and 'd_currentRecurringEvent' are // scheduled before 'now', choose 'd_currentEvent'. The behavior is // undefined if neither 'd_currentEvent' nor 'd_currentRecurringEvent' // is valid. Note that the argument and return value of this method // are expressed in terms of the number of microseconds elapsed since // some epoch, which is determined by the clock indicated at // construction (see {Supported Clock Types} in the component // documentation). Also note that this method may update the value of // 'now' with the current system time if necessary. void dispatchEvents(); // While d_running is true, execute events in the event and recurring // event queues at their scheduled times. Note that this method // implements the dispatching thread. void releaseCurrentEvents(); // Release 'd_currentRecurringEvent' and 'd_currentEvent', if they // refer to valid events. void scheduleEvent(EventHandle *event, const bsls::TimeInterval& epochTime, const EventData& eventData); // Schedule the callback of the specified 'eventData' to be dispatched // at the specified 'epochTime' truncated to microseconds. Load into // the specified 'event' pointer a handle that can be used to cancel // the event (by invoking 'cancelEvent'). The 'epochTime' 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 documentation). Note that if // 'epochTime' is in the past, the event is dispatched immediately. void scheduleEvent(const bsls::TimeInterval& epochTime, const EventData& eventData); // Schedule the callback of the specified 'eventData' to be dispatched // at the specified 'epochTime' truncated to microseconds. The // 'epochTime' 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 documentation). Note // that if 'epochTime' is in the past, the event is dispatched // immediately. void scheduleRecurringEvent(RecurringEventHandle *event, const RecurringEventData& eventData, const bsls::TimeInterval& startEpochTime); // Schedule a recurring event that invokes the callback of the // specified 'eventData' with the first event dispatched at the // specified 'startEpochTime' truncated to microseconds. Load into the // specified 'event' pointer a handle that can be used to cancel the // event (by invoking 'cancelEvent'). The 'startEpochTime' 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 documentation). The behavior is // undefined unless the interval of 'eventData' is at least one // microsecond. Note that if 'startEpochTime' is in the past, the // first event is dispatched immediately, and additional // '(now() - startEpochTime) / eventData.d_interval' events will be // submitted serially. void scheduleRecurringEventRaw(RecurringEvent **event, const RecurringEventData& eventData, const bsls::TimeInterval& startEpochTime); // Schedule a recurring event that invokes the callback of the // specified 'eventData' with the first event dispatched at the // specified 'startEpochTime' truncated to microseconds. Load into the // specified 'event' pointer a handle that can be used to cancel the // event (by invoking 'cancelEvent'). The 'startEpochTime' 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 documentation). The 'event' pointer // must be released by invoking 'releaseEventRaw' when it is no longer // needed. The behavior is undefined unless the interval of // 'eventData' is at least one microsecond. Note that if // 'startEpochTime' is in the past, the first event is dispatched // immediately, and additional // '(now() - startEpochTime) / eventData.d_interval' events will be // submitted serially. public: // TRAITS BSLMF_NESTED_TRAIT_DECLARATION(EventScheduler, bslma::UsesBslmaAllocator); // CREATORS explicit EventScheduler(bslma::Allocator *basicAllocator = 0); // Create an event scheduler using the default dispatcher functor (see // {The Dispatcher Thread and the Dispatcher Functor} in the // component-level documentation) and using the system realtime clock // to indicate the epoch used for all time intervals. Optionally // specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator is // used. explicit EventScheduler(bsls::SystemClockType::Enum clockType, bslma::Allocator *basicAllocator = 0); // Create an event scheduler using the default dispatcher functor (see // {The Dispatcher Thread and the Dispatcher Functor} in the // component-level documentation) and using 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. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY explicit EventScheduler( const bsl::chrono::system_clock&, bslma::Allocator *basicAllocator = 0); // Create an event scheduler using the default dispatcher functor (see // {The Dispatcher Thread and the Dispatcher Functor} in the // component-level documentation) and using the system realtime clock // to indicate the epoch used for all time intervals. Optionally // specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator is // used. explicit EventScheduler( const bsl::chrono::steady_clock&, bslma::Allocator *basicAllocator = 0); // Create an event scheduler using the default dispatcher functor (see // {The Dispatcher Thread and the Dispatcher Functor} in the // component-level documentation) and using the system monotonic clock // to indicate the epoch used for all time intervals. Optionally // specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator is // used. #endif explicit EventScheduler(const Dispatcher& dispatcherFunctor, bslma::Allocator *basicAllocator = 0); // Create an event scheduler using the specified 'dispatcherFunctor' // (see {The Dispatcher Thread and the Dispatcher Functor} in the // component-level documentation) and using the system realtime clock // to indicate the epoch used for all time intervals. Optionally // specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator is // used. EventScheduler(const Dispatcher& dispatcherFunctor, bsls::SystemClockType::Enum clockType, bslma::Allocator *basicAllocator = 0); // Create an event scheduler using the specified 'dispatcherFunctor' // (see {The Dispatcher Thread and the Dispatcher Functor} in the // component-level documentation) and using 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. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY EventScheduler(const Dispatcher& dispatcherFunctor, const bsl::chrono::system_clock&, bslma::Allocator *basicAllocator = 0); // Create an event scheduler using the specified 'dispatcherFunctor' // (see {The Dispatcher Thread and the Dispatcher Functor} in the // component-level documentation) and using the system realtime clock // to indicate the epoch used for all time intervals. Optionally // specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator is // used. EventScheduler(const Dispatcher& dispatcherFunctor, const bsl::chrono::steady_clock&, bslma::Allocator *basicAllocator = 0); // Create an event scheduler using the specified 'dispatcherFunctor' // (see {The Dispatcher Thread and the Dispatcher Functor} in the // component-level documentation) and using the system monotonic clock // to indicate the epoch used for all time intervals. Optionally // specify a 'basicAllocator' used to supply memory. If // 'basicAllocator' is 0, the currently installed default allocator is // used. #endif ~EventScheduler(); // Discard all unprocessed events and destroy this object. The // behavior is undefined unless the scheduler is stopped. // MANIPULATORS void cancelAllEvents(); // Cancel all recurring and one-time events scheduled in this // EventScheduler. void cancelAllEventsAndWait(); // Cancel all recurring and one-time events scheduled in this // EventScheduler. Block until all events have either been cancelled // or dispatched before this call returns. The behavior is undefined // if this method is invoked from the dispatcher thread. int cancelEvent(const Event *handle); int cancelEvent(const RecurringEvent *handle); // Cancel the event having the specified 'handle'. Return 0 on // successful cancellation, and a non-zero value if the 'handle' is // invalid *or* if the event has already been dispatched or canceled. // Note that due to the implicit conversion from Handle types, these // methods also match the following: //.. // int cancelEvent(const EventHandle& handle); // int cancelEvent(const RecurringEventHandle& handle); //.. // Compared to the version taking a pointer to Handle, the managed // reference to the event is not released until the Handle goes out of // scope. int cancelEvent(EventHandle *handle); int cancelEvent(RecurringEventHandle *handle); // Cancel the event having the specified 'handle' and release the // handle. Return 0 on successful cancellation, and a non-zero value // if the 'handle' is invalid *or* if the event has already been // dispatched or canceled. Note that 'handle' is released whether this // call is successful or not. int cancelEventAndWait(const Event *handle); int cancelEventAndWait(const RecurringEvent *handle); // Cancel the event having the specified 'handle'. Block until the // event having 'handle' (if it is valid) is either successfully // canceled or dispatched before the call returns. Return 0 on // successful cancellation, and a non-zero value if 'handle' is invalid // *or* if the event has already been dispatched or canceled. The // behavior is undefined if this method is invoked from the dispatcher // thread. Note that if the event is being executed when this method // is invoked, this method will block until it is completed and then // return a nonzero value. int cancelEventAndWait(EventHandle *handle); int cancelEventAndWait(RecurringEventHandle *handle); // Cancel the event having the specified 'handle' and release // '*handle'. Block until the event having 'handle' (if it is valid) // is either successfully canceled or dispatched before the call // returns. Return 0 on successful cancellation, and a non-zero value // if 'handle' is invalid *or* if the event has already been dispatched // or canceled. The behavior is undefined if this method is invoked // from the dispatcher thread. Note that if the event is being // executed when this method is invoked, this method will block until // it is completed and then return a nonzero value. Also note that it // is guaranteed that '*handle' will be released whether this call is // successful or not. void releaseEventRaw(Event *handle); void releaseEventRaw(RecurringEvent *handle); // Release the specified 'handle'. Every handle reference added by // 'scheduleEventRaw', 'addEventRefRaw', 'scheduleRecurringEventRaw', // or 'addRecurringEventRefRaw' must be released using this method to // avoid leaking resources. The behavior is undefined if the value of // 'handle' is used for any purpose after being released. int rescheduleEvent(const Event *handle, const bsls::TimeInterval& newEpochTime); // Reschedule the event referred to by the specified 'handle' at the // specified 'newEpochTime' truncated to microseconds. Return 0 on // successful reschedule, and a non-zero value if the 'handle' is // invalid *or* if the event has already been dispatched. The // 'newEpochTime' 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 // documentation). #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class t_CLOCK, class t_DURATION> int rescheduleEvent( const Event *handle, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& newEpochTime); // Reschedule the event referred to by the specified 'handle' at the // specified 'newEpochTime' truncated to microseconds. Return 0 on // successful reschedule, and a non-zero value if the 'handle' is // invalid *or* if the event has already been dispatched. The // 'newEpochTime' is an absolute time represented as an interval from // some epoch, determined by the clock associated with the time point. #endif int rescheduleEventAndWait(const Event *handle, const bsls::TimeInterval& newEpochTime); // Reschedule the event referred to by the specified 'handle' at the // specified 'newEpochTime' truncated to microseconds. Block until the // event having '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 'handle' is invalid // *or* if the event has already been dispatched. The 'newEpochTime' // 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 documentation). The // behavior is undefined if this method is invoked from the dispatcher // thread. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class t_CLOCK, class t_DURATION> int rescheduleEventAndWait( const Event *handle, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& newEpochTime); // Reschedule the event referred to by the specified 'handle' at the // specified 'newEpochTime' truncated to microseconds. Block until the // event having '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 'handle' is invalid // *or* if the event has already been dispatched. The 'newEpochTime' // is an absolute time represented as an interval from some epoch, // which is determined by the clock associated with the time point. // The behavior is undefined if this method is invoked from the // dispatcher thread. #endif void scheduleEvent(const bsls::TimeInterval& epochTime, const bsl::function<void()>& callback); void scheduleEvent(EventHandle *event, const bsls::TimeInterval& epochTime, const bsl::function<void()>& callback); // Schedule the specified 'callback' to be dispatched at the specified // 'epochTime' truncated to microseconds. Load into the optionally // specified 'event' a handle that can be used to cancel the event (by // invoking 'cancelEvent'). The 'epochTime' 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 documentation). This method guarantees that the // event will occur at or after 'epochTime'. 'epochTime' may be in the // past, in which case the event will be executed as soon as possible. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class t_CLOCK, class t_DURATION> void scheduleEvent( const bsl::chrono::time_point<t_CLOCK, t_DURATION>& epochTime, const bsl::function<void()>& callback); template <class t_CLOCK, class t_DURATION> void scheduleEvent( EventHandle *event, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& epochTime, const bsl::function<void()>& callback); // Schedule the specified 'callback' to be dispatched at the specified // 'epochTime' truncated to microseconds. Load into the optionally // specified 'event' a handle that can be used to cancel the event (by // invoking 'cancelEvent'). The 'epochTime' is an absolute time // represented as an interval from some epoch, which is determined by // the clock associated with the time point. This method guarantees // that the event will occur at or after 'epochTime'. 'epochTime' may // be in the past, in which case the event will be executed as soon as // possible. #endif void scheduleEventRaw(Event **event, const bsls::TimeInterval& epochTime, const bsl::function<void()>& callback); // Schedule the specified 'callback' to be dispatched at the specified // 'epochTime' truncated to microseconds. Load into the specified // 'event' pointer a handle that can be used to cancel the event (by // invoking 'cancelEvent'). The 'epochTime' 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 documentation). The 'event' pointer must be released // invoking 'releaseEventRaw' when it is no longer needed. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class t_CLOCK, class t_DURATION> void scheduleEventRaw( Event **event, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& epochTime, const bsl::function<void()>& callback); // Schedule the specified 'callback' to be dispatched at the specified // 'epochTime' truncated to microseconds. Load into the specified // 'event' pointer a handle that can be used to cancel the event (by // invoking 'cancelEvent'). The 'epochTime' is an absolute time // represented as an interval from some epoch, which is determined by // the clock associated with the time point. The 'event' pointer must // be released invoking 'releaseEventRaw' when it is no longer needed. #endif void scheduleRecurringEvent(const bsls::TimeInterval& interval, const bsl::function<void()>& callback, const bsls::TimeInterval& startEpochTime = bsls::TimeInterval(0)); void scheduleRecurringEvent(RecurringEventHandle *event, const bsls::TimeInterval& interval, const bsl::function<void()>& callback, const bsls::TimeInterval& startEpochTime = bsls::TimeInterval(0)); // Schedule a recurring event that invokes the specified 'callback' at // every specified 'interval' truncated to microseconds, with the first // event dispatched at the optionally specified 'startEpochTime' // truncated to microseconds. If 'startEpochTime' is not specified, // the first event is dispatched at one 'interval' from now. Load into // the optionally specified 'event' a handle that can be used to cancel // the event (by invoking 'cancelEvent'). The 'startEpochTime' 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 documentation). The behavior is // undefined unless 'interval' is at least one microsecond. Note that // if 'startEpochTime' is in the past, the first event is dispatched // immediately, and additional '(now() - startEpochTime) / interval' // events will be submitted serially. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class t_CLOCK, class t_REP_TYPE, class t_PERIOD_TYPE, class t_DURATION> void scheduleRecurringEvent( const bsl::chrono::duration<t_REP_TYPE, t_PERIOD_TYPE>& interval, const bsl::function<void()>& callback, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& startEpochTime = t_CLOCK::now()); template <class t_CLOCK, class t_REP_TYPE, class t_PERIOD_TYPE, class t_DURATION> void scheduleRecurringEvent( RecurringEventHandle *event, const bsl::chrono::duration<t_REP_TYPE, t_PERIOD_TYPE>& interval, const bsl::function<void()>& callback, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& startEpochTime = t_CLOCK::now()); // Schedule a recurring event that invokes the specified 'callback' at // every specified 'interval' truncated to microseconds, with the first // event dispatched at the optionally specified 'startEpochTime' // truncated to microseconds. If 'startEpochTime' is not specified, // the first event is dispatched at one 'interval' from now. Load into // the optionally specified 'event' a handle that can be used to cancel // the event (by invoking 'cancelEvent'). The 'startEpochTime' is an // absolute time represented as an interval from some epoch, which is // determined by the clock associated with the time point. The // behavior is undefined unless 'interval' is at least one microsecond. // Note that if 'startEpochTime' is in the past, the first event is // dispatched immediately, and additional // '(now() - startEpochTime) / interval' events will be submitted // serially. #endif void scheduleRecurringEventRaw( RecurringEvent **event, const bsls::TimeInterval& interval, const bsl::function<void()>& callback, const bsls::TimeInterval& startEpochTime = bsls::TimeInterval(0)); // Schedule a recurring event that invokes the specified 'callback' at // every specified 'interval' truncated to microseconds, with the first // event dispatched at the optionally specified 'startEpochTime' // truncated to microseconds. If 'startEpochTime' is not specified, // the first event is dispatched at one 'interval' from now. Load into // the specified 'event' pointer a handle that can be used to cancel // the event (by invoking 'cancelEvent'). The 'startEpochTime' 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 documentation). The 'event' pointer // must be released by invoking 'releaseEventRaw' when it is no longer // needed. The behavior is undefined unless 'interval' is at least one // microsecond. Note that if 'startEpochTime' is in the past, the // first event is dispatched immediately, and additional // '(now() - startEpochTime) / interval' events will be submitted // serially. #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class t_CLOCK, class t_REP_TYPE, class t_PERIOD_TYPE, class t_DURATION> void scheduleRecurringEventRaw( RecurringEvent **event, const bsl::chrono::duration<t_REP_TYPE, t_PERIOD_TYPE>& interval, const bsl::function<void()>& callback, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& startEpochTime = t_CLOCK::now()); // Schedule a recurring event that invokes the specified 'callback' at // every specified 'interval' truncated to microseconds, with the first // event dispatched at the optionally specified 'startEpochTime' // truncated to microseconds. If 'startEpochTime' is not specified, // the first event is dispatched at one 'interval' from now. Load into // the specified 'event' pointer a handle that can be used to cancel // the event (by invoking 'cancelEvent'). The 'startEpochTime' is an // absolute time represented as an interval from some epoch, which is // determined by the clock associated with the time point. The 'event' // pointer must be released by invoking 'releaseEventRaw' when it is no // longer needed. The behavior is undefined unless 'interval' is at // least one microsecond. Note that if 'startEpochTime' is in the // past, the first event is dispatched immediately, and additional // '(now() - startEpochTime) / interval' events will be submitted // serially. #endif 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. // ACCESSORS Event *addEventRefRaw(Event *handle) const; // Increment the reference count for the event referred to by the // specified 'handle' and return 'handle'. There must be a // corresponding call to 'releaseEventRaw' when the reference is no // longer needed. RecurringEvent *addRecurringEventRefRaw(RecurringEvent *handle) const; // Increment the reference count for the recurring event referred to by // the specified 'handle' and return 'handle'. There must be a // corresponding call to 'releaseEventRaw' when the reference is no // longer needed. bsls::SystemClockType::Enum clockType() const; // Return the value of the clock type that this object was created // with. bool isStarted() const; // Return 'true' if a call to 'start' has finished successfully more // recently than any call to 'stop', and 'false' otherwise. 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 numEvents() const; // Return the number of pending one-time events in this scheduler. int numRecurringEvents() const; // Return the number of recurring events registered with this // scheduler. bool isInDispatcherThread() const; // Return 'true' if the calling thread is the dispatcher thread of this // scheduler, and 'false' otherwise. // Aspects bslma::Allocator *allocator() const; // Return the allocator used by this object to supply memory. }; // =============================== // class EventSchedulerEventHandle // =============================== class EventSchedulerEventHandle { // Objects of this type refer to events in the 'EventScheduler' API. They // are convertible to 'const Event*' references and may be used in any // method that expects them. // PRIVATE TYPES typedef EventScheduler::EventQueue EventQueue; // DATA EventQueue::PairHandle d_handle; // FRIENDS friend class EventScheduler; public: // PUBLIC TYPES typedef EventScheduler::Event Event; // CREATORS EventSchedulerEventHandle(); // Create a new handle object that does not refer to an event. EventSchedulerEventHandle(const EventSchedulerEventHandle& original); // Create a new handle object referring to the same event as the // specified 'rhs' handle. ~EventSchedulerEventHandle(); // Destroy this object and release the managed reference, if any. // MANIPULATORS EventSchedulerEventHandle& operator=(const EventSchedulerEventHandle& rhs); // Release this handle's reference, if any; then make this handle refer // to the same event as the specified 'rhs' handle. Return a // modifiable reference to this handle. void release(); // Release the reference (if any) held by this object. // ACCESSORS operator const Event*() const; // Return a "raw" pointer to the event managed by this handle, or 0 if // this handle does not manage a reference. }; // ======================================== // class EventSchedulerRecurringEventHandle // ======================================== class EventSchedulerRecurringEventHandle { // Objects of this type refer to recurring events in the 'EventScheduler' // API. They are convertible to 'const RecurringEvent*' references and may // be used in any method which expects these. // PRIVATE TYPES typedef EventScheduler::RecurringEventData RecurringEventData; typedef bdlcc::SkipList<bsls::Types::Int64, RecurringEventData> RecurringEventQueue; // DATA RecurringEventQueue::PairHandle d_handle; // FRIENDS friend class EventScheduler; public: // PUBLIC TYPES typedef EventScheduler::RecurringEvent RecurringEvent; // CREATORS EventSchedulerRecurringEventHandle(); // Create a new handle object. EventSchedulerRecurringEventHandle( const EventSchedulerRecurringEventHandle& original); // Create a new handle object referring to the same recurring event as // the specified 'rhs' handle. ~EventSchedulerRecurringEventHandle(); // Destroy this object and release the managed reference, if any. // MANIPULATORS EventSchedulerRecurringEventHandle& operator=( const EventSchedulerRecurringEventHandle& rhs); // Release the reference managed by this handle, if any; then make this // handle refer to the same recurring event as the specified 'rhs' // handle. Return a modifiable reference to this event handle. void release(); // Release the reference managed by this handle, if any. // ACCESSORS operator const RecurringEvent*() const; // Return a "raw" pointer to the recurring event managed by this // handle, or 0 if this handle does not manage a reference. }; // ================================== // class EventSchedulerTestTimeSource // ================================== class EventSchedulerTestTimeSource { // 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 'EventSchedulerTestTimeSource' 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<EventSchedulerTestTimeSource_Data> d_data_p; // shared pointer to the state whose // lifetime must be as long as // '*this' and '*d_scheduler_p' EventScheduler *d_scheduler_p; // pointer to the scheduler that we // are augmenting public: // CREATORS explicit EventSchedulerTestTimeSource(EventScheduler *scheduler); // Create 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, notify the scheduler that the time has changed, and wait // for the scheduler to process the events triggered by this change in // time. Return the updated current-time value. The behavior is // undefined unless 'amount' is positive, and 'now + amount' is within // the range that can be represented with a 'bsls::TimeInterval'. // ACCESSORS bsls::TimeInterval now() const; // 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 // ============================================================================ // ------------------------------- // class EventSchedulerEventHandle // ------------------------------- // CREATORS inline EventSchedulerEventHandle::EventSchedulerEventHandle() { } inline EventSchedulerEventHandle::EventSchedulerEventHandle( const EventSchedulerEventHandle& original) : d_handle(original.d_handle) { } inline EventSchedulerEventHandle::~EventSchedulerEventHandle() { } // MANIPULATORS inline EventSchedulerEventHandle& EventSchedulerEventHandle::operator=(const EventSchedulerEventHandle& rhs) { d_handle = rhs.d_handle; return *this; } inline void EventSchedulerEventHandle::release() { d_handle.release(); } } // close package namespace // ACCESSORS // The scoping of "Event" below should not be necessary, but xlc (versions 8 // and 9) requires it inline bdlmt::EventSchedulerEventHandle:: operator const bdlmt::EventSchedulerEventHandle::Event*() const { return (const Event*)((const EventQueue::Pair*)d_handle); } namespace bdlmt { // ---------------------------------------- // class EventSchedulerRecurringEventHandle // ---------------------------------------- // CREATORS inline EventSchedulerRecurringEventHandle::EventSchedulerRecurringEventHandle() { } inline EventSchedulerRecurringEventHandle::EventSchedulerRecurringEventHandle( const EventSchedulerRecurringEventHandle& original) : d_handle(original.d_handle) { } inline EventSchedulerRecurringEventHandle::~EventSchedulerRecurringEventHandle() { } // MANIPULATORS inline void EventSchedulerRecurringEventHandle::release() { d_handle.release(); } inline EventSchedulerRecurringEventHandle& EventSchedulerRecurringEventHandle::operator=( const EventSchedulerRecurringEventHandle& rhs) { d_handle = rhs.d_handle; return *this; } } // close package namespace // ACCESSORS // The scoping of "RecurringEvent" below should not be necessary, but xlc // (versions 8 and 9) requires it inline bdlmt::EventSchedulerRecurringEventHandle::operator const bdlmt::EventSchedulerRecurringEventHandle::RecurringEvent*() const { return (const RecurringEvent*)((const RecurringEventQueue::Pair*)d_handle); } namespace bdlmt { // -------------------- // class EventScheduler // -------------------- #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY // PRIVATE CLASS METHODS template <class t_CLOCK, class t_DURATION> // not inline because it gets put into a bsl::function bsls::Types::Int64 EventScheduler::timeUntilTrigger( const bsl::chrono::time_point<t_CLOCK, t_DURATION>& absTime) { using namespace bsl::chrono; auto now = t_CLOCK::now(); microseconds offset = duration_cast<microseconds>(absTime - now); // If the time to fire is less than one microsecond in the future, then // report it as 1us. return 0 == offset.count() && absTime > now ? 1 : static_cast<bsls::Types::Int64>(offset.count()); } template <class t_CLOCK, class t_DURATION, class t_REP_TYPE, class t_PERIOD_TYPE> // not inline because it gets put into a bsl::function bsls::Types::Int64 EventScheduler::timeUntilTriggerRecurring( const bsl::chrono::time_point<t_CLOCK, t_DURATION>& absTime, const bsl::chrono::duration<t_REP_TYPE, t_PERIOD_TYPE>& interval, int eventIndex) { BSLS_ASSERT(0 <= eventIndex); return timeUntilTrigger(absTime + eventIndex * interval); } #endif // MANIPULATORS inline int EventScheduler::cancelEvent(const Event *handle) { const EventQueue::Pair *itemPtr = reinterpret_cast<const EventQueue::Pair*>( reinterpret_cast<const void*>(handle)); return d_eventQueue.remove(itemPtr); } inline int EventScheduler::cancelEvent(const RecurringEvent *handle) { const RecurringEventQueue::Pair *itemPtr = reinterpret_cast<const RecurringEventQueue::Pair*>( reinterpret_cast<const void*>(handle)); return d_recurringQueue.remove(itemPtr); } inline void EventScheduler::releaseEventRaw(Event *handle) { d_eventQueue.releaseReferenceRaw(reinterpret_cast<EventQueue::Pair*>( reinterpret_cast<void*>(handle))); } inline void EventScheduler::releaseEventRaw(RecurringEvent *handle) { d_recurringQueue.releaseReferenceRaw( reinterpret_cast<RecurringEventQueue::Pair*>( reinterpret_cast<void*>(handle))); } #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class t_CLOCK, class t_DURATION> int EventScheduler::rescheduleEvent( const Event *handle, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& newEpochTime) { BSLS_ASSERT(handle); if (bslmt::ChronoUtil::isMatchingClock<t_CLOCK>(d_clockType)) { return rescheduleEvent(handle, newEpochTime.time_since_epoch()); // RETURN } const EventQueue::Pair *h = reinterpret_cast<const EventQueue::Pair *>( reinterpret_cast<const void *>(handle)); bool isNewTop; bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex); bsls::TimeInterval offsetFromNow(newEpochTime - t_CLOCK::now()); if (h) { h->data().d_nowOffset = bdlf::BindUtil::bind( timeUntilTrigger<t_CLOCK, t_DURATION>, newEpochTime); } int ret = d_eventQueue.updateR(h, (now() + offsetFromNow).totalMicroseconds(), &isNewTop); if (0 == ret && isNewTop) { d_queueCondition.signal(); } return ret; } template <class t_CLOCK, class t_DURATION> int EventScheduler::rescheduleEventAndWait( const Event *handle, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& newEpochTime) { BSLS_ASSERT(handle); if (bslmt::ChronoUtil::isMatchingClock<t_CLOCK>(d_clockType)) { return rescheduleEventAndWait(handle, newEpochTime.time_since_epoch()); // RETURN } int ret; const EventQueue::Pair *h = reinterpret_cast<const EventQueue::Pair *>( reinterpret_cast<const void *>(handle)); { bool isNewTop; bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex); bsls::TimeInterval offsetFromNow(newEpochTime - t_CLOCK::now()); if (h) { h->data().d_nowOffset = bdlf::BindUtil::bind( timeUntilTrigger<t_CLOCK, t_DURATION>, newEpochTime); } ret = d_eventQueue.updateR(h, (now() + offsetFromNow).totalMicroseconds(), &isNewTop); if (0 == ret) { if (isNewTop) { d_queueCondition.signal(); } if (d_currentEvent != h) { return 0; // RETURN } } } // Wait until event is rescheduled or dispatched. bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex); while (1) { if (d_currentEvent != h) { break; } else { d_dispatcherAwaited = true; d_iterationCondition.wait(&d_mutex); } } return ret; } #endif inline void EventScheduler::scheduleEvent(const bsls::TimeInterval& epochTime, const bsl::function<void()>& callback) { scheduleEvent(epochTime, EventData(callback, EventScheduler::returnZero)); } inline void EventScheduler::scheduleEvent(EventHandle *event, const bsls::TimeInterval& epochTime, const bsl::function<void()>& callback) { scheduleEvent(event, epochTime, EventData(callback, returnZero)); } #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class t_CLOCK, class t_DURATION> inline void EventScheduler::scheduleEvent( const bsl::chrono::time_point<t_CLOCK, t_DURATION>& epochTime, const bsl::function<void()>& callback) { if (bslmt::ChronoUtil::isMatchingClock<t_CLOCK>(d_clockType)) { scheduleEvent(epochTime.time_since_epoch(), callback); } else { bsls::TimeInterval offsetFromNow(epochTime - t_CLOCK::now()); scheduleEvent(now() + offsetFromNow, EventData(callback, bdlf::BindUtil::bind( timeUntilTrigger<t_CLOCK, t_DURATION>, epochTime))); } } template <class t_CLOCK, class t_DURATION> void EventScheduler::scheduleEvent( EventHandle *event, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& epochTime, const bsl::function<void()>& callback) { BSLS_ASSERT(event); if (bslmt::ChronoUtil::isMatchingClock<t_CLOCK>(d_clockType)) { scheduleEvent(event, epochTime.time_since_epoch(), callback); } else { bsls::TimeInterval offsetFromNow(epochTime - t_CLOCK::now()); scheduleEvent(event, now() + offsetFromNow, EventData(callback, bdlf::BindUtil::bind( timeUntilTrigger<t_CLOCK, t_DURATION>, epochTime))); } } template <class t_CLOCK, class t_DURATION> void EventScheduler::scheduleEventRaw( Event **event, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& epochTime, const bsl::function<void()>& callback) { BSLS_ASSERT(event); if (bslmt::ChronoUtil::isMatchingClock<t_CLOCK>(d_clockType)) { scheduleEventRaw(event, epochTime.time_since_epoch(), callback); } else { using namespace bsl::chrono; microseconds stime = duration_cast<microseconds>(epochTime.time_since_epoch()); bool newTop; d_eventQueue.addRawR( (EventQueue::Pair **)event, (bsls::Types::Int64)stime.count(), EventData( callback, bdlf::BindUtil::bind(timeUntilTrigger<t_CLOCK, t_DURATION>, epochTime)), &newTop); if (newTop) { bslmt::LockGuard<bslmt::Mutex> lock(&d_mutex); d_queueCondition.signal(); } } } #endif inline void EventScheduler::scheduleRecurringEvent( const bsls::TimeInterval& interval, const bsl::function<void()>& callback, const bsls::TimeInterval& startEpochTime) { // Note that when this review is converted to an assert, the following // assert is redundant and can be removed. BSLS_REVIEW(1 <= interval.totalMicroseconds()); BSLS_ASSERT(0 != interval); scheduleRecurringEventRaw(0, interval, callback, startEpochTime); } inline void EventScheduler::scheduleRecurringEvent( RecurringEventHandle *event, const bsls::TimeInterval& interval, const bsl::function<void()>& callback, const bsls::TimeInterval& startEpochTime) { // Note that when this review is converted to an assert, the following // assert is redundant and can be removed. BSLS_REVIEW(1 <= interval.totalMicroseconds()); BSLS_ASSERT(0 != interval); BSLS_ASSERT(event); scheduleRecurringEvent( event, RecurringEventData(interval, callback, returnZeroInt), startEpochTime); } #ifdef BSLS_LIBRARYFEATURES_HAS_CPP11_BASELINE_LIBRARY template <class t_CLOCK, class t_REP_TYPE, class t_PERIOD_TYPE, class t_DURATION> void EventScheduler::scheduleRecurringEvent( const bsl::chrono::duration<t_REP_TYPE, t_PERIOD_TYPE>& interval, const bsl::function<void()>& callback, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& startEpochTime) { BSLS_ASSERT(bsl::chrono::microseconds(1) <= interval); if (bslmt::ChronoUtil::isMatchingClock<t_CLOCK>(d_clockType)) { scheduleRecurringEvent(interval, callback, startEpochTime.time_since_epoch()); } else { using namespace bsl::chrono; bsls::TimeInterval offsetFromNow(startEpochTime - t_CLOCK::now()); scheduleRecurringEventRaw( 0, RecurringEventData( interval, callback, bdlf::BindUtil::bind(timeUntilTriggerRecurring<t_CLOCK, t_DURATION, t_REP_TYPE, t_PERIOD_TYPE>, startEpochTime, interval, bdlf::PlaceHolders::_1)), now() + offsetFromNow); } } template <class t_CLOCK, class t_REP_TYPE, class t_PERIOD_TYPE, class t_DURATION> void EventScheduler::scheduleRecurringEvent( RecurringEventHandle *event, const bsl::chrono::duration<t_REP_TYPE, t_PERIOD_TYPE>& interval, const bsl::function<void()>& callback, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& startEpochTime) { BSLS_ASSERT(event); BSLS_ASSERT(bsl::chrono::microseconds(1) <= interval); if (bslmt::ChronoUtil::isMatchingClock<t_CLOCK>(d_clockType)) { scheduleRecurringEvent(event, interval, callback, startEpochTime.time_since_epoch()); } else { using namespace bsl::chrono; bsls::TimeInterval offsetFromNow(startEpochTime - t_CLOCK::now()); scheduleRecurringEvent( event, RecurringEventData( interval, callback, bdlf::BindUtil::bind(timeUntilTriggerRecurring<t_CLOCK, t_DURATION, t_REP_TYPE, t_PERIOD_TYPE>, startEpochTime, interval, bdlf::PlaceHolders::_1)), now() + offsetFromNow); } } template <class t_CLOCK, class t_REP_TYPE, class t_PERIOD_TYPE, class t_DURATION> void EventScheduler::scheduleRecurringEventRaw( RecurringEvent **event, const bsl::chrono::duration<t_REP_TYPE, t_PERIOD_TYPE>& interval, const bsl::function<void()>& callback, const bsl::chrono::time_point<t_CLOCK, t_DURATION>& startEpochTime) { BSLS_ASSERT(event); BSLS_ASSERT(bsl::chrono::microseconds(1) <= interval); if (bslmt::ChronoUtil::isMatchingClock<t_CLOCK>(d_clockType)) { scheduleRecurringEventRaw(event, interval, callback, startEpochTime.time_since_epoch()); } else { using namespace bsl::chrono; bsls::TimeInterval offsetFromNow(startEpochTime - t_CLOCK::now()); scheduleRecurringEventRaw( event, RecurringEventData( interval, callback, bdlf::BindUtil::bind(timeUntilTriggerRecurring<t_CLOCK, t_DURATION, t_REP_TYPE, t_PERIOD_TYPE>, startEpochTime, interval, bdlf::PlaceHolders::_1)), now() + offsetFromNow); } } #endif // ACCESSORS inline EventScheduler::Event* EventScheduler::addEventRefRaw(Event *handle) const { EventQueue::Pair *h = reinterpret_cast<EventQueue::Pair*>( reinterpret_cast<void*>(handle)); return reinterpret_cast<Event*>(d_eventQueue.addPairReferenceRaw(h)); } inline EventScheduler::RecurringEvent* EventScheduler::addRecurringEventRefRaw(RecurringEvent *handle) const { RecurringEventQueue::Pair *h = reinterpret_cast<RecurringEventQueue::Pair*>( reinterpret_cast<void*>(handle)); return reinterpret_cast<RecurringEvent*>( d_recurringQueue.addPairReferenceRaw(h)); } inline bsls::SystemClockType::Enum EventScheduler::clockType() const { return d_clockType; } inline bsls::TimeInterval EventScheduler::now() const { return d_currentTimeFunctor(); } inline int EventScheduler::numEvents() const { return d_eventQueue.length(); } inline int EventScheduler::numRecurringEvents() const { return d_recurringQueue.length(); } inline bool EventScheduler::isInDispatcherThread() const { return d_dispatcherThreadId.loadAcquire() == bslmt::ThreadUtil::selfIdAsUint64(); } // Aspects inline bslma::Allocator *EventScheduler::allocator() const { return d_eventQueue.allocator(); } } // close package namespace } // close enterprise namespace #endif // ---------------------------------------------------------------------------- // Copyright 2019 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 ----------------------------------