#ifndef __Timers_h_ #define __Timers_h_ #include "../../shared/MiscSupport.h" #include "../../shared/ThreadClass.h" #include "../../shared/Messages.h" #include "../../shared/SelectableRequestQueue.h" /* This was inspired by Timers.pas. This is not as generially applicable, * in partiuclar because it always sends a broadcast message. However, that * should be enough for the alert server. Hopefully this will be a little * simpler. We use our standard queuing architecture here. No locks. If * you cancel a request, you might still get the callback, because it was * already in the queue. We use the standard broadcast mechanism, because * the code is already used to that. Messages can get sent, and the listener * can go away before receiving them. */ class DataNodeManager; class TimerThread : private ThreadClass { private: enum { mtCancelTimer, mtTimerEvent, mtQuit }; SelectableRequestQueue _incoming; DataNodeManager *const _manager; class CancelTimerRequest : public Request { public: CancelTimerRequest() : Request(NULL) { callbackId = mtCancelTimer; } std::string channel; }; class TimerEvent : public Request { public: TimerEvent() : Request(NULL) { callbackId = mtTimerEvent; } std::string channel; TimeVal nextTime; int periodMS; bool absoluteTime; bool periodic() const { return periodMS; } }; std::set< std::pair< TimeVal, TimerEvent * > > _byTime; std::map< std::string, TimerEvent * > _byChannel; void cancelTimerRequest(std::string const &channel, bool andDelete = true); void addTimerRequest(TimerEvent *toAdd); // This takes ownership of the pointer. It will be deleted or added back // into the queue. We assume this pointer is not already in the queue. void fireEvent(TimerEvent *event); protected: void threadFunction(); public: // This has the simple job of canceling a request in its destructor. You // can create one of these objects if you don't want to forget to cancel. // Canceling is not that important for one time events, but you don't want // to waste time on recurring events going to a destination which is gone. // This can be converted to a string. This allows you to use this object, // rather than a string, when you are requesting or canceling a broadcast. class Helper : NoCopy, NoAssign { private: const std::string _channel; TimerThread *const _timerThread; public: Helper(std::string const &channel, TimerThread *timerThread) : _channel(channel), _timerThread(timerThread) { } ~Helper() { _timerThread->cancel(_channel); } operator std::string() const { return _channel; } }; // There can be at most one outstanding broadcast request per channel. // Creating a new request automatically cancels any old ones. This is for // simplicity. This way we don't have to have a seperate token for canceling // a request. You can request or cancel a broadcast from any thread. The // resulting broadcast will always come in the correct thread for the data // node manager. void requestPeriodicBroadcast(std::string const &channel, int periodMS, TimeVal firstTime = false); void requestBroadcastAfter(std::string const &channel, int delayMS); void requestBroadcastAt(std::string const &channel, TimeVal firstTime); void cancel(std::string const &channel); TimerThread(DataNodeManager *manager); ~TimerThread(); }; #endif