Line data Source code
1 : /* 2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. 3 : */ 4 : 5 : // Timer implementation using ASIO and Task infrastructure. 6 : // Registers an ASIO timer. On ASIO timer expiry, a task will be created to 7 : // run the timer. Supports user specified task-id. 8 : // 9 : // Operations supported 10 : // - Create a timer by allocating an object of type Timer 11 : // - Start a timer 12 : // If a timer is already running, it is a no op. In order to auto-restart 13 : // a timer, return 'tru'e from timer callback, false otherwise 14 : // 15 : // There can be atmost one "Task" outstanding for the timer. 16 : // 17 : // - Cancel a timer 18 : // Cancels a running timer. Both ASIO registerations and task spawned will 19 : // be cancelled. 20 : // 21 : // If timer is already fired, "Cancel" api will fail and return 'false' 22 : // 23 : // - Delete a timer 24 : // Cancels the timer and triggers deletion of the timer. Application should 25 : // not access the timer after its deleted 26 : // 27 : // Concurrency aspects: 28 : // - Timer is allocated by application 29 : // - Applications must call TimerManager::DeleteTimer() to delete the timer 30 : // - All operations on timer are protected by mutex 31 : // - When timer is running, it can have references from ASIO and Task. 32 : // Timer class will keep of reference from ASIO and Task. Timer will 33 : // be deleted when both the references go away. (via intrusive pointer) 34 : // 35 : 36 : #ifndef TIMER_H_ 37 : #define TIMER_H_ 38 : 39 : #include <atomic> 40 : #include <mutex> 41 : #include <set> 42 : 43 : #include <boost/asio/placeholders.hpp> 44 : #include <boost/bind/bind.hpp> 45 : #include <boost/intrusive_ptr.hpp> 46 : #include <boost/scoped_ptr.hpp> 47 : #include <boost/function.hpp> 48 : #include <boost/asio.hpp> 49 : #include <boost/system/error_code.hpp> 50 : 51 : #include <base/task.h> 52 : 53 : using namespace boost::placeholders; 54 : 55 : class TimerImpl; 56 : 57 : class Timer { 58 : private: 59 : // Task used to fire the timer 60 : class TimerTask; 61 : 62 : public: 63 : typedef boost::function<bool(void)> Handler; 64 : typedef boost::function<void(std::string, std::string, std::string)> 65 : ErrorHandler; 66 : 67 : Timer(boost::asio::io_context &service, const std::string &name, 68 : int task_id, int task_instance, bool delete_on_completion = false); 69 : virtual ~Timer(); 70 : 71 : // Start a timer 72 : // If timer already started, try cancelling and restart 73 : // 74 : // Timer callback routine handler should return true to start the timer 75 : // again, false otherwise 76 : // 77 : // XXX Timer cannot be Start()ed from timer callback routine itself. 78 : // Return true from the callback in order to post the timer again. This 79 : // would use the same time, the timer was initially started with 80 : // 81 : bool Start(int time, Handler handler, ErrorHandler error_handler = NULL); 82 : 83 : //Can be called only from callback 84 : bool Reschedule(int time); 85 : 86 : // Cancel a running timer 87 : bool Cancel(); 88 : 89 93535 : bool running() const { 90 93535 : std::scoped_lock lock(mutex_); 91 93539 : return (state_ == Running); 92 93539 : } 93 : 94 336465 : bool fired() const { 95 336465 : std::scoped_lock lock(mutex_); 96 336465 : return (state_ == Fired); 97 336465 : } 98 : 99 16 : int time() const { 100 16 : return time_; 101 : } 102 : 103 22887 : bool cancelled() const { 104 22887 : std::scoped_lock lock(mutex_); 105 22895 : return (state_ == Cancelled); 106 22892 : } 107 : 108 0 : bool Idle() const { 109 0 : std::scoped_lock lock(mutex_); 110 0 : return (state_ == Cancelled || state_ == Init); 111 0 : } 112 : 113 0 : bool IsDeleteOnCompletion() const { 114 0 : return delete_on_completion_; 115 : } 116 : 117 : int64_t GetElapsedTime() const; 118 : 119 : // Only for state machine test 120 : // XXX: Don't use in production code 121 516 : void Fire() { 122 516 : std::scoped_lock lock(mutex_); 123 516 : if (handler_ && !handler_.empty()) { 124 508 : SetState(Fired); 125 508 : handler_(); 126 508 : SetState(Init); 127 : } 128 516 : } 129 : 130 0 : virtual std::string Description() { 131 0 : return name_; 132 : } 133 : private: 134 : friend class TimerManager; 135 : friend class TimerTest; 136 : 137 : friend void intrusive_ptr_add_ref(Timer *timer); 138 : friend void intrusive_ptr_release(Timer *timer); 139 : typedef boost::intrusive_ptr<Timer> TimerPtr; 140 : 141 : enum TimerState { 142 : Init = 0, 143 : Running = 1, 144 : Fired = 2, 145 : Cancelled = 3, 146 : }; 147 : 148 : // ASIO callback on timer expiry. Start a task to serve the timer 149 : void StartTimerTask(TimerPtr reference, 150 : int time, uint32_t seq_no, 151 : const boost::system::error_code &ec); 152 : 153 10123193 : void SetState(TimerState s) { state_ = s; } 154 24816 : static int GetTimerInstanceId() { return -1; } 155 24816 : static int GetTimerTaskId() { 156 : static int timer_task_id = -1; 157 : 158 24816 : if (timer_task_id == -1) { 159 79 : TaskScheduler *scheduler = TaskScheduler::GetInstance(); 160 79 : timer_task_id = scheduler->GetTaskId("timer::TimerTask"); 161 : } 162 24816 : return timer_task_id; 163 : } 164 : 165 : boost::scoped_ptr<TimerImpl> impl_; 166 : std::string name_; 167 : Handler handler_; 168 : ErrorHandler error_handler_; 169 : mutable std::mutex mutex_; 170 : TimerState state_; 171 : TimerTask *timer_task_; 172 : int time_; 173 : int task_id_; 174 : int task_instance_; 175 : uint32_t seq_no_; 176 : bool delete_on_completion_; 177 : std::atomic<int> refcount_; 178 : }; 179 : 180 51974511 : inline void intrusive_ptr_add_ref(Timer *timer) { 181 51974511 : timer->refcount_++; 182 51986432 : } 183 : 184 51980769 : inline void intrusive_ptr_release(Timer *timer) { 185 51980769 : int prev = timer->refcount_.fetch_sub(1); 186 51980769 : if (prev == 1) { 187 376965 : delete timer; 188 : } 189 51980768 : } 190 : 191 : // 192 : // TimerManager is the place holder for all the Timer objects 193 : // instantiated in the life time of a process 194 : // 195 : // Timer objects are help in TimerSet until all the cleanup is complete 196 : // and only then should they be deleted via DeleteTimer() API 197 : // 198 : // Since Timer objects are also held by boost::asio routines, they are 199 : // protected using intrusive pointers 200 : // 201 : // This is similar to how TcpSession objects are managed via TcpSessionPtr 202 : // 203 : class TimerManager { 204 : public: 205 : static Timer *CreateTimer(boost::asio::io_context &service, 206 : const std::string &name, 207 : int task_id = Timer::GetTimerTaskId(), 208 : int task_instance = Timer::GetTimerInstanceId(), 209 : bool delete_on_completion = false); 210 : static bool DeleteTimer(Timer *Timer); 211 : 212 : private: 213 : friend class TimerTest; 214 : 215 : typedef boost::intrusive_ptr<Timer> TimerPtr; 216 : struct TimerPtrCmp { 217 7205566 : bool operator()(const TimerPtr &lhs, 218 : const TimerPtr &rhs) const { 219 7205566 : return lhs.get() < rhs.get(); 220 : } 221 : }; 222 : typedef std::set<TimerPtr, TimerPtrCmp> TimerSet; 223 : static void AddTimer(Timer *Timer); 224 : 225 : static std::mutex mutex_; 226 : static TimerSet timer_ref_; 227 : }; 228 : 229 : #endif /* TIMER_H_ */