xref: /trunk/main/vos/source/timer.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include <osl/time.h>
29 
30 #include <vos/timer.hxx>
31 #include <vos/diagnose.hxx>
32 #include <vos/ref.hxx>
33 #include <vos/thread.hxx>
34 #include <vos/conditn.hxx>
35 
36 
37 /////////////////////////////////////////////////////////////////////////////
38 //
39 // Timer manager
40 //
41 
42 class OTimerManagerCleanup;
43 
44 class vos::OTimerManager : public vos::OThread
45 {
46 
47 public:
48 
49     ///
50     OTimerManager();
51 
52     ///
53     ~OTimerManager();
54 
55     /// register timer
56     sal_Bool SAL_CALL registerTimer(vos::OTimer* pTimer);
57 
58     /// unregister timer
59     sal_Bool SAL_CALL unregisterTimer(vos::OTimer* pTimer);
60 
61     /// lookup timer
62     sal_Bool SAL_CALL lookupTimer(const vos::OTimer* pTimer);
63 
64     /// retrieves the "Singleton" TimerManager Instance
65     static OTimerManager* SAL_CALL getTimerManager();
66 
67 
68 protected:
69 
70     /// worker-function of thread
71     virtual void SAL_CALL run();
72 
73     // Checking and triggering of a timer event
74     void SAL_CALL checkForTimeout();
75 
76     // cleanup Method
77     virtual void SAL_CALL onTerminated();
78 
79     // sorted-queue data
80     vos::OTimer*        m_pHead;
81     // List Protection
82     vos::OMutex     m_Lock;
83     // Signal the insertion of a timer
84     vos::OCondition m_notEmpty;
85 
86     // Synchronize access to OTimerManager
87     static vos::OMutex m_Access;
88 
89     // "Singleton Pattern"
90     static vos::OTimerManager* m_pManager;
91 
92     friend class OTimerManagerCleanup;
93 
94 };
95 
96 using namespace vos;
97 
98 /////////////////////////////////////////////////////////////////////////////
99 //
100 // Timer class
101 //
102 
103 VOS_IMPLEMENT_CLASSINFO(VOS_CLASSNAME(OTimer, vos),
104                         VOS_NAMESPACE(OTimer, vos),
105                         VOS_NAMESPACE(OObject, vos), 0);
106 
107 OTimer::OTimer()
108 {
109     m_TimeOut     = 0;
110     m_Expired     = 0;
111     m_RepeatDelta = 0;
112     m_pNext       = 0;
113 }
114 
115 OTimer::OTimer(const TTimeValue& Time)
116 {
117     m_TimeOut     = Time;
118     m_RepeatDelta = 0;
119     m_Expired     = 0;
120     m_pNext       = 0;
121 
122     m_TimeOut.normalize();
123 }
124 
125 OTimer::OTimer(const TTimeValue& Time, const TTimeValue& Repeat)
126 {
127     m_TimeOut     = Time;
128     m_RepeatDelta = Repeat;
129     m_Expired     = 0;
130     m_pNext       = 0;
131 
132     m_TimeOut.normalize();
133     m_RepeatDelta.normalize();
134 }
135 
136 OTimer::~OTimer()
137 {
138     stop();
139 }
140 
141 void OTimer::start()
142 {
143     if (! isTicking())
144     {
145         if (! m_TimeOut.isEmpty())
146             setRemainingTime(m_TimeOut);
147 
148         OTimerManager *pManager = OTimerManager::getTimerManager();
149 
150         VOS_ASSERT(pManager);
151 
152         if ( pManager != 0 )
153         {
154             pManager->registerTimer(this);
155         }
156     }
157 }
158 
159 void OTimer::stop()
160 {
161     OTimerManager *pManager = OTimerManager::getTimerManager();
162 
163     VOS_ASSERT(pManager);
164 
165     if ( pManager != 0 )
166     {
167         pManager->unregisterTimer(this);
168     }
169 }
170 
171 sal_Bool OTimer::isTicking() const
172 {
173     OTimerManager *pManager = OTimerManager::getTimerManager();
174 
175     VOS_ASSERT(pManager);
176 
177     if (pManager)
178         return pManager->lookupTimer(this);
179     else
180         return sal_False;
181 
182 }
183 
184 sal_Bool OTimer::isExpired() const
185 {
186     TTimeValue Now;
187 
188     osl_getSystemTime(&Now);
189 
190     return !(Now < m_Expired);
191 }
192 
193 sal_Bool OTimer::expiresBefore(const OTimer* pTimer) const
194 {
195     VOS_ASSERT(pTimer);
196 
197     if ( pTimer != 0 )
198     {
199         return m_Expired < pTimer->m_Expired;
200     }
201     else
202     {
203         return sal_False;
204     }
205 }
206 
207 void OTimer::setAbsoluteTime(const TTimeValue& Time)
208 {
209     m_TimeOut     = 0;
210     m_Expired     = Time;
211     m_RepeatDelta = 0;
212 
213     m_Expired.normalize();
214 }
215 
216 void OTimer::setRemainingTime(const TTimeValue& Remaining)
217 {
218     osl_getSystemTime(&m_Expired);
219 
220     m_Expired.addTime(Remaining);
221 }
222 
223 void OTimer::setRemainingTime(const TTimeValue& Remaining, const TTimeValue& Repeat)
224 {
225     osl_getSystemTime(&m_Expired);
226 
227     m_Expired.addTime(Remaining);
228 
229     m_RepeatDelta = Repeat;
230 }
231 
232 void OTimer::addTime(const TTimeValue& Delta)
233 {
234     m_Expired.addTime(Delta);
235 }
236 
237 TTimeValue OTimer::getRemainingTime() const
238 {
239     TTimeValue Now;
240 
241     osl_getSystemTime(&Now);
242 
243     sal_Int32 secs = m_Expired.Seconds - Now.Seconds;
244 
245     if (secs < 0)
246         return TTimeValue(0, 0);
247 
248     sal_Int32 nsecs = m_Expired.Nanosec - Now.Nanosec;
249 
250     if (nsecs < 0)
251     {
252         if (secs > 0)
253         {
254             secs  -= 1;
255             nsecs += 1000000000;
256         }
257         else
258             return TTimeValue(0, 0);
259     }
260 
261     return TTimeValue(secs, nsecs);
262 }
263 
264 
265 /////////////////////////////////////////////////////////////////////////////
266 //
267 // Timer manager
268 //
269 
270 OMutex vos::OTimerManager::m_Access;
271 OTimerManager* vos::OTimerManager::m_pManager=0;
272 
273 OTimerManager::OTimerManager()
274 {
275     OGuard Guard(&m_Access);
276 
277     VOS_ASSERT(m_pManager == 0);
278 
279     m_pManager = this;
280 
281     m_pHead= 0;
282 
283     m_notEmpty.reset();
284 
285     // start thread
286     create();
287 }
288 
289 OTimerManager::~OTimerManager()
290 {
291     OGuard Guard(&m_Access);
292 
293     if ( m_pManager == this )
294         m_pManager = 0;
295 }
296 
297 void OTimerManager::onTerminated()
298 {
299     delete this; // mfe: AAARRRGGGHHH!!!
300 }
301 
302 OTimerManager* OTimerManager::getTimerManager()
303 {
304     OGuard Guard(&m_Access);
305 
306     if (! m_pManager)
307         new OTimerManager;
308 
309     return (m_pManager);
310 }
311 
312 sal_Bool OTimerManager::registerTimer(OTimer* pTimer)
313 {
314     VOS_ASSERT(pTimer);
315 
316     if ( pTimer == 0 )
317     {
318         return sal_False;
319     }
320 
321     OGuard Guard(&m_Lock);
322 
323     // try to find one with equal or lower remaining time.
324     OTimer** ppIter = &m_pHead;
325 
326     while (*ppIter)
327     {
328         if (pTimer->expiresBefore(*ppIter))
329         {
330             // next element has higher remaining time,
331             // => insert new timer before
332             break;
333         }
334         ppIter= &((*ppIter)->m_pNext);
335     }
336 
337     // next element has higher remaining time,
338     // => insert new timer before
339     pTimer->m_pNext= *ppIter;
340     *ppIter = pTimer;
341 
342 
343     if (pTimer == m_pHead)
344     {
345         // it was inserted as new head
346         // signal it to TimerManager Thread
347         m_notEmpty.set();
348     }
349 
350     return sal_True;
351 }
352 
353 sal_Bool OTimerManager::unregisterTimer(OTimer* pTimer)
354 {
355     VOS_ASSERT(pTimer);
356 
357     if ( pTimer == 0 )
358     {
359         return sal_False;
360     }
361 
362     // lock access
363     OGuard Guard(&m_Lock);
364 
365     OTimer** ppIter = &m_pHead;
366 
367     while (*ppIter)
368     {
369         if (pTimer == (*ppIter))
370         {
371             // remove timer from list
372             *ppIter = (*ppIter)->m_pNext;
373             return sal_True;
374         }
375         ppIter= &((*ppIter)->m_pNext);
376     }
377 
378     return sal_False;
379 }
380 
381 sal_Bool OTimerManager::lookupTimer(const OTimer* pTimer)
382 {
383     VOS_ASSERT(pTimer);
384 
385     if ( pTimer == 0 )
386     {
387         return sal_False;
388     }
389 
390     // lock access
391     OGuard Guard(&m_Lock);
392 
393     // check the list
394     for (OTimer* pIter = m_pHead; pIter != 0; pIter= pIter->m_pNext)
395     {
396         if (pIter == pTimer)
397         {
398             return sal_True;
399         }
400     }
401 
402     return sal_False;
403 }
404 
405 void OTimerManager::checkForTimeout()
406 {
407 
408     m_Lock.acquire();
409 
410     if ( m_pHead == 0 )
411     {
412         m_Lock.release();
413         return;
414     }
415 
416     OTimer* pTimer = m_pHead;
417 
418     if (pTimer->isExpired())
419     {
420         // remove expired timer
421         m_pHead = pTimer->m_pNext;
422 
423         pTimer->acquire();
424 
425         m_Lock.release();
426 
427         pTimer->onShot();
428 
429         // restart timer if specified
430         if ( ! pTimer->m_RepeatDelta.isEmpty() )
431         {
432             TTimeValue Now;
433 
434             osl_getSystemTime(&Now);
435 
436             Now.Seconds += pTimer->m_RepeatDelta.Seconds;
437             Now.Nanosec += pTimer->m_RepeatDelta.Nanosec;
438 
439             pTimer->m_Expired = Now;
440 
441             registerTimer(pTimer);
442         }
443         pTimer->release();
444     }
445     else
446     {
447         m_Lock.release();
448     }
449 
450 
451     return;
452 }
453 
454 void OTimerManager::run()
455 {
456     setPriority(TPriority_BelowNormal);
457 
458     while (schedule())
459     {
460         TTimeValue      delay;
461         TTimeValue*     pDelay=0;
462 
463 
464         m_Lock.acquire();
465 
466         if (m_pHead != 0)
467         {
468             delay = m_pHead->getRemainingTime();
469             pDelay=&delay;
470         }
471         else
472         {
473             pDelay=0;
474         }
475 
476 
477         m_notEmpty.reset();
478 
479         m_Lock.release();
480 
481 
482         m_notEmpty.wait(pDelay);
483 
484         checkForTimeout();
485     }
486 
487 }
488 
489 
490 /////////////////////////////////////////////////////////////////////////////
491 //
492 // Timer manager cleanup
493 //
494 
495 // jbu:
496 // The timer manager cleanup has been removed (no thread is killed anymore).
497 // So the thread leaks.
498 // This will result in a GPF in case the vos-library gets unloaded before
499 // process termination.
500 // -> TODO : rewrite this file, so that the timerManager thread gets destroyed,
501 //           when there are no timers anymore !
502