xref: /trunk/main/vos/source/timer.cxx (revision cdf0e10c)
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