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 : #include "base/timer.h"
7 : #include "base/timer_impl.h"
8 :
9 : class Timer::TimerTask : public Task {
10 : public:
11 59081 : TimerTask(TimerPtr timer, boost::system::error_code ec)
12 59081 : : Task(timer->task_id_, timer->task_instance_), timer_(timer), ec_(ec) {
13 59081 : }
14 :
15 118162 : virtual ~TimerTask() {
16 118162 : }
17 :
18 : // Invokes user callback.
19 : // Timer could have been cancelled or delete when task was enqueued
20 58921 : virtual bool Run() {
21 : {
22 58921 : std::unique_lock<std::mutex> lock(timer_->mutex_);
23 :
24 : // cancelled task .. ignore
25 58926 : if (task_cancelled()) {
26 : // Cancelled timer's task releases the ownership of the timer
27 2 : lock.unlock();
28 2 : timer_ = NULL;
29 2 : return true;
30 : }
31 :
32 : // Conditions to invoke user callback met. Fire it
33 58900 : timer_->SetState(Timer::Fired);
34 58904 : }
35 :
36 58924 : bool restart = false;
37 :
38 : // TODO: Is this error needed by user?
39 58924 : if (ec_ && !timer_->error_handler_.empty()) {
40 0 : timer_->error_handler_(timer_->name_,
41 0 : std::string(ec_.category().name()),
42 0 : ec_.message());
43 : } else {
44 58910 : restart = timer_->handler_();
45 : }
46 :
47 58928 : OnTaskCancel();
48 :
49 58922 : if (restart) {
50 67161 : timer_->Start(timer_->time_, timer_->handler_,
51 33597 : timer_->error_handler_);
52 25315 : } else if (timer_->delete_on_completion_) {
53 165 : TimerManager::DeleteTimer(timer_.get());
54 : }
55 58957 : return true;
56 : }
57 :
58 : // Task Cancelled/Destroyed when it was Fired.
59 58915 : void OnTaskCancel() {
60 58915 : if (!timer_) {
61 2 : return;
62 : }
63 58907 : std::scoped_lock lock(timer_->mutex_);
64 :
65 58896 : if (timer_->timer_task_ != this) {
66 0 : assert(!timer_->timer_task_);
67 : }
68 :
69 58874 : timer_->timer_task_ = NULL;
70 58859 : timer_->SetState(Timer::Init);
71 58840 : }
72 :
73 0 : virtual std::string Description() const {
74 0 : return timer_->Description();
75 : }
76 :
77 : private:
78 : TimerPtr timer_;
79 : boost::system::error_code ec_;
80 : DISALLOW_COPY_AND_ASSIGN(TimerTask);
81 : };
82 :
83 379719 : Timer::Timer(boost::asio::io_context &service, const std::string &name,
84 379719 : int task_id, int task_instance, bool delete_on_completion)
85 379719 : : impl_(new TimerImpl(service)),
86 379717 : name_(name),
87 379715 : handler_(NULL),
88 379715 : error_handler_(NULL),
89 379715 : state_(Init),
90 379715 : timer_task_(NULL),
91 379715 : time_(0),
92 379715 : task_id_(task_id),
93 379715 : task_instance_(task_instance),
94 379715 : seq_no_(0),
95 759434 : delete_on_completion_(delete_on_completion) {
96 379715 : refcount_ = 0;
97 379718 : }
98 :
99 756596 : Timer::~Timer() {
100 378298 : assert(state_ != Running && state_ != Fired);
101 756596 : }
102 :
103 : //
104 : // Start a timer
105 : //
106 : // If the timer is already running, return silently
107 : //
108 4683050 : bool Timer::Start(int time, Handler handler, ErrorHandler error_handler) {
109 4683050 : std::scoped_lock lock(mutex_);
110 :
111 4683268 : if (time < 0) {
112 138 : return true;
113 : }
114 :
115 4683130 : if (state_ == Running || state_ == Fired) {
116 174725 : return true;
117 : }
118 :
119 : // Restart the timer
120 4508405 : time_ = time;
121 4508405 : handler_ = handler;
122 4507768 : seq_no_++;
123 4507768 : error_handler_ = error_handler;
124 4507556 : boost::system::error_code ec;
125 4507556 : impl_->expires_from_now(time, ec);
126 4508397 : if (ec) {
127 0 : return false;
128 : }
129 :
130 4508374 : SetState(Running);
131 9016729 : impl_->async_wait(
132 9016799 : boost::bind(&Timer::StartTimerTask, this, TimerPtr(this),
133 : time, seq_no_, boost::asio::placeholders::error));
134 4508494 : return true;
135 4683357 : }
136 :
137 10702 : bool Timer::Reschedule(int time)
138 : {
139 10702 : if (state_ != Fired)
140 183 : return false;
141 :
142 10519 : if (time < 0)
143 0 : return false;
144 :
145 10519 : time_ = time;
146 10519 : return true;
147 : }
148 :
149 : // Cancel a running timer
150 5371422 : bool Timer::Cancel() {
151 5371422 : std::scoped_lock lock(mutex_);
152 :
153 : // A fired timer cannot be cancelled
154 5371432 : if (state_ == Fired) {
155 4815 : return false;
156 : }
157 :
158 : // Cancel Task. If Task cancel succeeds, there will be no callback.
159 : // Reset TaskRef if call succeeds.
160 5366617 : if (timer_task_) {
161 : TaskScheduler::CancelReturnCode rc =
162 122 : TaskScheduler::GetInstance()->Cancel(timer_task_);
163 122 : assert(rc != TaskScheduler::FAILED);
164 122 : timer_task_ = NULL;
165 : }
166 :
167 5366617 : SetState(Cancelled);
168 5366566 : return true;
169 5371381 : }
170 :
171 : // ASIO callback on timer expiry. Start a task to serve the timer
172 4429100 : void Timer::StartTimerTask(TimerPtr reference, int time, uint32_t seq_no,
173 : const boost::system::error_code &ec) {
174 4429100 : std::scoped_lock lock(mutex_);
175 :
176 4429100 : if (state_ == Cancelled) {
177 25791 : return;
178 : }
179 :
180 : // If timer was cancelled, no callback is invoked
181 4403309 : if (ec && ec.value() == boost::asio::error::operation_aborted) {
182 4344228 : return;
183 : }
184 :
185 : // Timer could have fired for previous run. Validate the seq_no_
186 59081 : if (seq_no_ != seq_no) {
187 0 : return;
188 : }
189 : // Start a task and add Task reference.
190 59081 : assert(timer_task_ == NULL);
191 59081 : timer_task_ = new TimerTask(reference, ec);
192 59081 : TaskScheduler::GetInstance()->Enqueue(timer_task_);
193 4429100 : }
194 :
195 : //
196 : // TimerManager class routines
197 : //
198 : TimerManager::TimerSet TimerManager::timer_ref_;
199 : std::mutex TimerManager::mutex_;
200 :
201 379718 : Timer *TimerManager::CreateTimer(
202 : boost::asio::io_context &service, const std::string &name,
203 : int task_id, int task_instance, bool delete_on_completion) {
204 : Timer *timer = new Timer(service, name, task_id, task_instance,
205 379718 : delete_on_completion);
206 379717 : AddTimer(timer);
207 379719 : return timer;
208 : }
209 :
210 379717 : void TimerManager::AddTimer(Timer *timer) {
211 379717 : std::scoped_lock lock(mutex_);
212 379719 : timer_ref_.insert(TimerPtr(timer));
213 :
214 759438 : return;
215 379719 : }
216 :
217 : //
218 : // Delete a timer object from the data base, by removing the intrusive
219 : // reference. If any other objects has a reference to this timer such as
220 : // boost::asio, the timer object deletion is automatically deferred
221 : //
222 374118 : bool TimerManager::DeleteTimer(Timer *timer) {
223 374118 : if (!timer || timer->fired()) return false;
224 :
225 362350 : if (!timer->Cancel() && timer->IsDeleteOnCompletion())
226 0 : return false;
227 :
228 362350 : std::scoped_lock lock(mutex_);
229 362350 : timer_ref_.erase(TimerPtr(timer));
230 :
231 362350 : return true;
232 362350 : }
233 :
234 : // Get timer's already elapsed time in milliseconds.
235 729 : int64_t Timer::GetElapsedTime() const {
236 729 : std::scoped_lock lock(mutex_);
237 : int64_t elapsed;
238 :
239 : #if __cplusplus >= 201103L
240 729 : elapsed = std::chrono::nanoseconds(impl_->expires_from_now()).count();
241 : #else
242 : elapsed = boost::chrono::nanoseconds(impl_->expires_from_now()).count();
243 : #endif
244 :
245 729 : elapsed = time_ - elapsed / 1000000; // Convert nanoseconds to milliseconds.
246 729 : if (elapsed < 0)
247 0 : elapsed = 0;
248 729 : return elapsed;
249 729 : }
|