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 queue().clear();
87 _finished.set();
88 return true;
89 }
90
91 void wait()
92 {
93 _finished.wait();
94 }
95
96private:
97 Poco::Event _finished;
98};
99
100
101class TaskNotification: public TimerNotification
102{
103public:
104 TaskNotification(Poco::TimedNotificationQueue& queue, TimerTask::Ptr pTask):
105 TimerNotification(queue),
106 _pTask(pTask)
107 {
108 }
109
110 ~TaskNotification()
111 {
112 }
113
114 TimerTask::Ptr task()
115 {
116 return _pTask;
117 }
118
119 bool execute()
120 {
121 if (!_pTask->isCancelled())
122 {
123 try
124 {
125 _pTask->_lastExecution.update();
126 _pTask->run();
127 }
128 catch (Exception& exc)
129 {
130 ErrorHandler::handle(exc);
131 }
132 catch (std::exception& exc)
133 {
134 ErrorHandler::handle(exc);
135 }
136 catch (...)
137 {
138 ErrorHandler::handle();
139 }
140 }
141 return true;
142 }
143
144private:
145 TimerTask::Ptr _pTask;
146};
147
148
149class PeriodicTaskNotification: public TaskNotification
150{
151public:
152 PeriodicTaskNotification(Poco::TimedNotificationQueue& queue, TimerTask::Ptr pTask, long interval):
153 TaskNotification(queue, pTask),
154 _interval(interval)
155 {
156 }
157
158 ~PeriodicTaskNotification()
159 {
160 }
161
162 bool execute()
163 {
164 TaskNotification::execute();
165
166 if (!task()->isCancelled())
167 {
168 Poco::Clock now;
169 Poco::Clock nextExecution;
170 nextExecution += static_cast<Poco::Clock::ClockDiff>(_interval)*1000;
171 if (nextExecution < now) nextExecution = now;
172 queue().enqueueNotification(this, nextExecution);
173 duplicate();
174 }
175 return true;
176 }
177
178private:
179 long _interval;
180};
181
182
183class FixedRateTaskNotification: public TaskNotification
184{
185public:
186 FixedRateTaskNotification(Poco::TimedNotificationQueue& queue, TimerTask::Ptr pTask, long interval, Poco::Clock clock):
187 TaskNotification(queue, pTask),
188 _interval(interval),
189 _nextExecution(clock)
190 {
191 }
192
193 ~FixedRateTaskNotification()
194 {
195 }
196
197 bool execute()
198 {
199 TaskNotification::execute();
200
201 if (!task()->isCancelled())
202 {
203 Poco::Clock now;
204 _nextExecution += static_cast<Poco::Clock::ClockDiff>(_interval)*1000;
205 if (_nextExecution < now) _nextExecution = now;
206 queue().enqueueNotification(this, _nextExecution);
207 duplicate();
208 }
209 return true;
210 }
211
212private:
213 long _interval;
214 Poco::Clock _nextExecution;
215};
216
217
218Timer::Timer()
219{
220 _thread.start(*this);
221}
222
223
224Timer::Timer(Poco::Thread::Priority priority)
225{
226 _thread.setPriority(priority);
227 _thread.start(*this);
228}
229
230
231Timer::Timer(int prio, int policy)
232{
233 _thread.setOSPriority(prio, policy);
234 _thread.start(*this);
235}
236
237
238Timer::~Timer()
239{
240 try
241 {
242 _queue.enqueueNotification(new StopNotification(_queue), Poco::Clock(0));
243 _thread.join();
244 }
245 catch (...)
246 {
247 poco_unexpected();
248 }
249}
250
251
252void Timer::cancel(bool wait)
253{
254 Poco::AutoPtr<CancelNotification> pNf = new CancelNotification(_queue);
255 _queue.enqueueNotification(pNf, Poco::Clock(0));
256 if (wait)
257 {
258 pNf->wait();
259 }
260}
261
262
263void Timer::schedule(TimerTask::Ptr pTask, Poco::Timestamp time)
264{
265 validateTask(pTask);
266 _queue.enqueueNotification(new TaskNotification(_queue, pTask), time);
267}
268
269
270void Timer::schedule(TimerTask::Ptr pTask, Poco::Clock clock)
271{
272 validateTask(pTask);
273 _queue.enqueueNotification(new TaskNotification(_queue, pTask), clock);
274}
275
276
277void Timer::schedule(TimerTask::Ptr pTask, long delay, long interval)
278{
279 Poco::Clock clock;
280 clock += static_cast<Poco::Clock::ClockDiff>(delay)*1000;
281 schedule(pTask, clock, interval);
282}
283
284
285void Timer::schedule(TimerTask::Ptr pTask, Poco::Timestamp time, long interval)
286{
287 validateTask(pTask);
288 _queue.enqueueNotification(new PeriodicTaskNotification(_queue, pTask, interval), time);
289}
290
291
292void Timer::schedule(TimerTask::Ptr pTask, Poco::Clock clock, long interval)
293{
294 validateTask(pTask);
295 _queue.enqueueNotification(new PeriodicTaskNotification(_queue, pTask, interval), clock);
296}
297
298
299void Timer::scheduleAtFixedRate(TimerTask::Ptr pTask, long delay, long interval)
300{
301 Poco::Clock clock;
302 clock += static_cast<Poco::Clock::ClockDiff>(delay)*1000;
303 scheduleAtFixedRate(pTask, clock, interval);
304}
305
306
307void Timer::scheduleAtFixedRate(TimerTask::Ptr pTask, Poco::Timestamp time, long interval)
308{
309 validateTask(pTask);
310 Poco::Timestamp tsNow;
311 Poco::Clock clock;
312 Poco::Timestamp::TimeDiff diff = time - tsNow;
313 clock += diff;
314 _queue.enqueueNotification(new FixedRateTaskNotification(_queue, pTask, interval, clock), clock);
315}
316
317
318void Timer::scheduleAtFixedRate(TimerTask::Ptr pTask, Poco::Clock clock, long interval)
319{
320 validateTask(pTask);
321 _queue.enqueueNotification(new FixedRateTaskNotification(_queue, pTask, interval, clock), clock);
322}
323
324
325void Timer::run()
326{
327 bool cont = true;
328 while (cont)
329 {
330 Poco::AutoPtr<TimerNotification> pNf = static_cast<TimerNotification*>(_queue.waitDequeueNotification());
331 cont = pNf->execute();
332 }
333}
334
335
336void Timer::validateTask(const TimerTask::Ptr& pTask)
337{
338 if (pTask->isCancelled())
339 {
340 throw Poco::IllegalStateException("A cancelled task must not be rescheduled");
341 }
342}
343
344
345} } // namespace Poco::Util
346