1//
2// Timer.cpp
3//
4// Library: Util
5// Package: Timer
6// Module: Timer
7//
8// Copyright (c) 2009, Applied Informatics Software Engineering GmbH.
9// and Contributors.
10//
11// SPDX-License-Identifier: BSL-1.0
12//
13
14
15#include "Poco/Util/Timer.h"
16#include "Poco/Notification.h"
17#include "Poco/ErrorHandler.h"
18#include "Poco/Event.h"
19
20
21using Poco::ErrorHandler;
22
23
24namespace Poco {
25namespace Util {
26
27
28class TimerNotification: public Poco::Notification
29{
30public:
31 TimerNotification(Poco::TimedNotificationQueue& queue):
32 _queue(queue)
33 {
34 }
35
36 ~TimerNotification()
37 {
38 }
39
40 virtual bool execute() = 0;
41
42 Poco::TimedNotificationQueue& queue()
43 {
44 return _queue;
45 }
46
47private:
48 Poco::TimedNotificationQueue& _queue;
49};
50
51
52class StopNotification: public TimerNotification
53{
54public:
55 StopNotification(Poco::TimedNotificationQueue& queue):
56 TimerNotification(queue)
57 {
58 }
59
60 ~StopNotification()
61 {
62 }
63
64 bool execute()
65 {
66 queue().clear();
67 return false;
68 }
69};
70
71
72class CancelNotification: public TimerNotification
73{
74public:
75 CancelNotification(Poco::TimedNotificationQueue& queue):
76 TimerNotification(queue)
77 {
78 }
79
80 ~CancelNotification()
81 {
82 }
83
84 bool execute()
85 {
86 // Check if there's a StopNotification pending.
87 Poco::AutoPtr<TimerNotification> pNf = static_cast<TimerNotification*>(queue().dequeueNotification());
88 while (pNf)
89 {
90 if (pNf.cast<StopNotification>())
91 {
92 queue().clear();
93 _finished.set();
94 return false;
95 }
96 pNf = static_cast<TimerNotification*>(queue().dequeueNotification());
97 }
98
99 queue().clear();
100 _finished.set();
101 return true;
102 }
103
104 void wait()
105 {
106 _finished.wait();
107 }
108
109private:
110 Poco::Event _finished;
111};
112
113
114class TaskNotification: public TimerNotification
115{
116public:
117 TaskNotification(Poco::TimedNotificationQueue& queue, TimerTask::Ptr pTask):
118 TimerNotification(queue),
119 _pTask(pTask)
120 {
121 }
122
123 ~TaskNotification()
124 {
125 }
126
127 TimerTask::Ptr task()
128 {
129 return _pTask;
130 }
131
132 bool execute()
133 {
134 if (!_pTask->isCancelled())
135 {
136 try
137 {
138 _pTask->_lastExecution.update();
139 _pTask->run();
140 }
141 catch (Exception& exc)
142 {
143 ErrorHandler::handle(exc);
144 }
145 catch (std::exception& exc)
146 {
147 ErrorHandler::handle(exc);
148 }
149 catch (...)
150 {
151 ErrorHandler::handle();
152 }
153 }
154 return true;
155 }
156
157private:
158 TimerTask::Ptr _pTask;
159};
160
161
162class PeriodicTaskNotification: public TaskNotification
163{
164public:
165 PeriodicTaskNotification(Poco::TimedNotificationQueue& queue, TimerTask::Ptr pTask, long interval):
166 TaskNotification(queue, pTask),
167 _interval(interval)
168 {
169 }
170
171 ~PeriodicTaskNotification()
172 {
173 }
174
175 bool execute()
176 {
177 TaskNotification::execute();
178
179 if (!task()->isCancelled())
180 {
181 Poco::Clock now;
182 Poco::Clock nextExecution;
183 nextExecution += static_cast<Poco::Clock::ClockDiff>(_interval)*1000;
184 if (nextExecution < now) nextExecution = now;
185 queue().enqueueNotification(this, nextExecution);
186 duplicate();
187 }
188 return true;
189 }
190
191private:
192 long _interval;
193};
194
195
196class FixedRateTaskNotification: public TaskNotification
197{
198public:
199 FixedRateTaskNotification(Poco::TimedNotificationQueue& queue, TimerTask::Ptr pTask, long interval, Poco::Clock clock):
200 TaskNotification(queue, pTask),
201 _interval(interval),
202 _nextExecution(clock)
203 {
204 }
205
206 ~FixedRateTaskNotification()
207 {
208 }
209
210 bool execute()
211 {
212 TaskNotification::execute();
213
214 if (!task()->isCancelled())
215 {
216 Poco::Clock now;
217 _nextExecution += static_cast<Poco::Clock::ClockDiff>(_interval)*1000;
218 if (_nextExecution < now) _nextExecution = now;
219 queue().enqueueNotification(this, _nextExecution);
220 duplicate();
221 }
222 return true;
223 }
224
225private:
226 long _interval;
227 Poco::Clock _nextExecution;
228};
229
230
231Timer::Timer()
232{
233 _thread.start(*this);
234}
235
236
237Timer::Timer(Poco::Thread::Priority priority)
238{
239 _thread.setPriority(priority);
240 _thread.start(*this);
241}
242
243
244Timer::Timer(int prio, int policy)
245{
246 _thread.setOSPriority(prio, policy);
247 _thread.start(*this);
248}
249
250
251Timer::~Timer()
252{
253 try
254 {
255 _queue.enqueueNotification(new StopNotification(_queue), Poco::Clock(0));
256 _thread.join();
257 }
258 catch (...)
259 {
260 poco_unexpected();
261 }
262}
263
264
265void Timer::cancel(bool wait)
266{
267 Poco::AutoPtr<CancelNotification> pNf = new CancelNotification(_queue);
268 _queue.enqueueNotification(pNf, Poco::Clock(0));
269 if (wait)
270 {
271 pNf->wait();
272 }
273}
274
275
276void Timer::schedule(TimerTask::Ptr pTask, Poco::Timestamp time)
277{
278 validateTask(pTask);
279 _queue.enqueueNotification(new TaskNotification(_queue, pTask), time);
280}
281
282
283void Timer::schedule(TimerTask::Ptr pTask, Poco::Clock clock)
284{
285 validateTask(pTask);
286 _queue.enqueueNotification(new TaskNotification(_queue, pTask), clock);
287}
288
289
290void Timer::schedule(TimerTask::Ptr pTask, long delay, long interval)
291{
292 Poco::Clock clock;
293 clock += static_cast<Poco::Clock::ClockDiff>(delay)*1000;
294 schedule(pTask, clock, interval);
295}
296
297
298void Timer::schedule(TimerTask::Ptr pTask, Poco::Timestamp time, long interval)
299{
300 validateTask(pTask);
301 _queue.enqueueNotification(new PeriodicTaskNotification(_queue, pTask, interval), time);
302}
303
304
305void Timer::schedule(TimerTask::Ptr pTask, Poco::Clock clock, long interval)
306{
307 validateTask(pTask);
308 _queue.enqueueNotification(new PeriodicTaskNotification(_queue, pTask, interval), clock);
309}
310
311
312void Timer::scheduleAtFixedRate(TimerTask::Ptr pTask, long delay, long interval)
313{
314 Poco::Clock clock;
315 clock += static_cast<Poco::Clock::ClockDiff>(delay)*1000;
316 scheduleAtFixedRate(pTask, clock, interval);
317}
318
319
320void Timer::scheduleAtFixedRate(TimerTask::Ptr pTask, Poco::Timestamp time, long interval)
321{
322 validateTask(pTask);
323 Poco::Timestamp tsNow;
324 Poco::Clock clock;
325 Poco::Timestamp::TimeDiff diff = time - tsNow;
326 clock += diff;
327 _queue.enqueueNotification(new FixedRateTaskNotification(_queue, pTask, interval, clock), clock);
328}
329
330
331void Timer::scheduleAtFixedRate(TimerTask::Ptr pTask, Poco::Clock clock, long interval)
332{
333 validateTask(pTask);
334 _queue.enqueueNotification(new FixedRateTaskNotification(_queue, pTask, interval, clock), clock);
335}
336
337
338void Timer::run()
339{
340 bool cont = true;
341 while (cont)
342 {
343 Poco::AutoPtr<TimerNotification> pNf = static_cast<TimerNotification*>(_queue.waitDequeueNotification());
344 cont = pNf->execute();
345 }
346}
347
348
349void Timer::validateTask(const TimerTask::Ptr& pTask)
350{
351 if (pTask->isCancelled())
352 {
353 throw Poco::IllegalStateException("A cancelled task must not be rescheduled");
354 }
355}
356
357
358} } // namespace Poco::Util
359