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