1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19 #include <salhelper/timer.hxx>
20
21 #include <osl/thread.hxx>
22 #include <osl/conditn.hxx>
23 #include <osl/mutex.hxx>
24 #include <rtl/instance.hxx>
25
26 using namespace salhelper;
27
28 class salhelper::TimerManager : public osl::Thread
29 {
30 public:
31 TimerManager();
32
33 virtual ~TimerManager() override;
34
35 /// register timer
36 void registerTimer(salhelper::Timer* pTimer);
37
38 /// unregister timer
39 void unregisterTimer(salhelper::Timer const * pTimer);
40
41 /// lookup timer
42 bool lookupTimer(const salhelper::Timer* pTimer);
43
44 /// retrieves the "Singleton" TimerManager Instance
45 static TimerManager* getTimerManager();
46
47 protected:
48 /// worker-function of thread
49 virtual void SAL_CALL run() override;
50
51 /// Checking and triggering of a timer event
52 void checkForTimeout();
53
54 /// cleanup Method
55 virtual void SAL_CALL onTerminated() override;
56
57 /// sorted-queue data
58 salhelper::Timer* m_pHead;
59 /// List Protection
60 osl::Mutex m_Lock;
61 /// Signal the insertion of a timer
62 osl::Condition m_notEmpty;
63
64 /// "Singleton Pattern"
65 static salhelper::TimerManager* m_pManager;
66
67 };
68
Timer()69 Timer::Timer()
70 : m_aTimeOut(0),
71 m_aExpired(0),
72 m_aRepeatDelta(0),
73 m_pNext(nullptr)
74 {
75 }
76
Timer(const TTimeValue & rTime)77 Timer::Timer(const TTimeValue& rTime)
78 : m_aTimeOut(rTime),
79 m_aExpired(0),
80 m_aRepeatDelta(0),
81 m_pNext(nullptr)
82 {
83 }
84
Timer(const TTimeValue & rTime,const TTimeValue & Repeat)85 Timer::Timer(const TTimeValue& rTime, const TTimeValue& Repeat)
86 : m_aTimeOut(rTime),
87 m_aExpired(0),
88 m_aRepeatDelta(Repeat),
89 m_pNext(nullptr)
90 {
91 }
92
~Timer()93 Timer::~Timer()
94 {
95 stop();
96 }
97
start()98 void Timer::start()
99 {
100 if (!isTicking())
101 {
102 if (!m_aTimeOut.isEmpty())
103 setRemainingTime(m_aTimeOut);
104
105 TimerManager *pManager = TimerManager::getTimerManager();
106
107 if (pManager)
108 pManager->registerTimer(this);
109 }
110 }
111
stop()112 void Timer::stop()
113 {
114 TimerManager *pManager = TimerManager::getTimerManager();
115
116 if (pManager)
117 pManager->unregisterTimer(this);
118 }
119
isTicking() const120 sal_Bool Timer::isTicking() const
121 {
122 TimerManager *pManager = TimerManager::getTimerManager();
123
124 if (pManager)
125 return pManager->lookupTimer(this);
126 else
127 return false;
128 }
129
isExpired() const130 sal_Bool Timer::isExpired() const
131 {
132 TTimeValue Now;
133
134 osl_getSystemTime(&Now);
135
136 return !(Now < m_aExpired);
137 }
138
expiresBefore(const Timer * pTimer) const139 sal_Bool Timer::expiresBefore(const Timer* pTimer) const
140 {
141 if (pTimer)
142 return m_aExpired < pTimer->m_aExpired;
143 else
144 return false;
145 }
146
setAbsoluteTime(const TTimeValue & Time)147 void Timer::setAbsoluteTime(const TTimeValue& Time)
148 {
149 m_aTimeOut = 0;
150 m_aExpired = Time;
151 m_aRepeatDelta = 0;
152
153 m_aExpired.normalize();
154 }
155
setRemainingTime(const TTimeValue & Remaining)156 void Timer::setRemainingTime(const TTimeValue& Remaining)
157 {
158 osl_getSystemTime(&m_aExpired);
159
160 m_aExpired.addTime(Remaining);
161 }
162
setRemainingTime(const TTimeValue & Remaining,const TTimeValue & Repeat)163 void Timer::setRemainingTime(const TTimeValue& Remaining, const TTimeValue& Repeat)
164 {
165 osl_getSystemTime(&m_aExpired);
166
167 m_aExpired.addTime(Remaining);
168
169 m_aRepeatDelta = Repeat;
170 }
171
addTime(const TTimeValue & Delta)172 void Timer::addTime(const TTimeValue& Delta)
173 {
174 m_aExpired.addTime(Delta);
175 }
176
getRemainingTime() const177 TTimeValue Timer::getRemainingTime() const
178 {
179 TTimeValue Now;
180
181 osl_getSystemTime(&Now);
182
183 sal_Int32 secs = m_aExpired.Seconds - Now.Seconds;
184
185 if (secs < 0)
186 return TTimeValue(0, 0);
187
188 sal_Int32 nsecs = m_aExpired.Nanosec - Now.Nanosec;
189
190 if (nsecs < 0)
191 {
192 if (secs > 0)
193 {
194 secs -= 1;
195 nsecs += 1000000000;
196 }
197 else
198 return TTimeValue(0, 0);
199 }
200
201 return TTimeValue(secs, nsecs);
202 }
203
204 namespace
205 {
206 // Synchronize access to TimerManager
207 struct theTimerManagerMutex : public rtl::Static< osl::Mutex, theTimerManagerMutex> {};
208 }
209
210 TimerManager* salhelper::TimerManager::m_pManager = nullptr;
211
212 /** The timer manager cleanup has been removed (no thread is killed anymore),
213 so the thread leaks.
214
215 This will result in a GPF in case the salhelper-library gets unloaded before
216 process termination.
217
218 @TODO : rewrite this file, so that the timerManager thread gets destroyed,
219 when there are no timers anymore !
220 **/
221
TimerManager()222 TimerManager::TimerManager()
223 {
224 osl::MutexGuard Guard(theTimerManagerMutex::get());
225
226 assert(m_pManager == nullptr);
227
228 m_pManager = this;
229 m_pHead= nullptr;
230 m_notEmpty.reset();
231
232 // start thread
233 create();
234 }
235
~TimerManager()236 TimerManager::~TimerManager()
237 {
238 osl::MutexGuard Guard(theTimerManagerMutex::get());
239
240 if (m_pManager == this)
241 m_pManager = nullptr;
242 }
243
onTerminated()244 void TimerManager::onTerminated()
245 {
246 delete this; // FIXME
247 }
248
getTimerManager()249 TimerManager* TimerManager::getTimerManager()
250 {
251 osl::MutexGuard Guard(theTimerManagerMutex::get());
252
253 if (! m_pManager)
254 new TimerManager;
255
256 return m_pManager;
257 }
258
registerTimer(Timer * pTimer)259 void TimerManager::registerTimer(Timer* pTimer)
260 {
261 if (!pTimer)
262 return;
263
264 osl::MutexGuard Guard(m_Lock);
265
266 // try to find one with equal or lower remaining time.
267 Timer** ppIter = &m_pHead;
268
269 while (*ppIter)
270 {
271 if (pTimer->expiresBefore(*ppIter))
272 {
273 // next element has higher remaining time,
274 // => insert new timer before
275 break;
276 }
277 ppIter= &((*ppIter)->m_pNext);
278 }
279
280 // next element has higher remaining time,
281 // => insert new timer before
282 pTimer->m_pNext= *ppIter;
283 *ppIter = pTimer;
284
285
286 if (pTimer == m_pHead)
287 {
288 // it was inserted as new head
289 // signal it to TimerManager Thread
290 m_notEmpty.set();
291 }
292 }
293
unregisterTimer(Timer const * pTimer)294 void TimerManager::unregisterTimer(Timer const * pTimer)
295 {
296 if (!pTimer)
297 return;
298
299 // lock access
300 osl::MutexGuard Guard(m_Lock);
301
302 Timer** ppIter = &m_pHead;
303
304 while (*ppIter)
305 {
306 if (pTimer == (*ppIter))
307 {
308 // remove timer from list
309 *ppIter = (*ppIter)->m_pNext;
310 return;
311 }
312 ppIter= &((*ppIter)->m_pNext);
313 }
314 }
315
lookupTimer(const Timer * pTimer)316 bool TimerManager::lookupTimer(const Timer* pTimer)
317 {
318 if (!pTimer)
319 return false;
320
321 // lock access
322 osl::MutexGuard Guard(m_Lock);
323
324 // check the list
325 for (Timer* pIter = m_pHead; pIter != nullptr; pIter= pIter->m_pNext)
326 {
327 if (pIter == pTimer)
328 return true;
329 }
330
331 return false;
332 }
333
checkForTimeout()334 void TimerManager::checkForTimeout()
335 {
336 m_Lock.acquire();
337
338 if (!m_pHead)
339 {
340 m_Lock.release();
341 return;
342 }
343
344 Timer* pTimer = m_pHead;
345
346 if (pTimer->isExpired())
347 {
348 // remove expired timer
349 m_pHead = pTimer->m_pNext;
350
351 pTimer->acquire();
352
353 m_Lock.release();
354
355 pTimer->onShot();
356
357 // restart timer if specified
358 if (!pTimer->m_aRepeatDelta.isEmpty())
359 {
360 TTimeValue Now;
361
362 osl_getSystemTime(&Now);
363
364 Now.Seconds += pTimer->m_aRepeatDelta.Seconds;
365 Now.Nanosec += pTimer->m_aRepeatDelta.Nanosec;
366
367 pTimer->m_aExpired = Now;
368
369 registerTimer(pTimer);
370 }
371 pTimer->release();
372 }
373 else
374 {
375 m_Lock.release();
376 }
377 }
378
run()379 void TimerManager::run()
380 {
381 osl_setThreadName("salhelper::TimerManager");
382
383 setPriority( osl_Thread_PriorityBelowNormal );
384
385 while (schedule())
386 {
387 TTimeValue delay;
388 TTimeValue* pDelay=nullptr;
389
390 m_Lock.acquire();
391
392 if (m_pHead != nullptr)
393 {
394 delay = m_pHead->getRemainingTime();
395 pDelay=&delay;
396 }
397 else
398 {
399 pDelay=nullptr;
400 }
401
402
403 m_notEmpty.reset();
404
405 m_Lock.release();
406
407
408 m_notEmpty.wait(pDelay);
409
410 checkForTimeout();
411 }
412
413 }
414
415 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
416