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