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