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