bump product version to 6.3.0.0.beta1
[LibreOffice.git] / salhelper / source / timer.cxx
blob8101b23875fb4fb6b29429a79b798723e07671fb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
19 #include <salhelper/timer.hxx>
21 #include <osl/thread.hxx>
22 #include <osl/conditn.hxx>
23 #include <osl/mutex.hxx>
24 #include <rtl/instance.hxx>
26 using namespace salhelper;
28 class salhelper::TimerManager : public osl::Thread
30 public:
31 TimerManager();
33 virtual ~TimerManager() override;
35 /// register timer
36 void registerTimer(salhelper::Timer* pTimer);
38 /// unregister timer
39 void unregisterTimer(salhelper::Timer const * pTimer);
41 /// lookup timer
42 bool lookupTimer(const salhelper::Timer* pTimer);
44 /// retrieves the "Singleton" TimerManager Instance
45 static TimerManager* getTimerManager();
47 protected:
48 /// worker-function of thread
49 virtual void SAL_CALL run() override;
51 /// Checking and triggering of a timer event
52 void checkForTimeout();
54 /// cleanup Method
55 virtual void SAL_CALL onTerminated() override;
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;
64 /// "Singleton Pattern"
65 static salhelper::TimerManager* m_pManager;
69 Timer::Timer()
70 : m_aTimeOut(0),
71 m_aExpired(0),
72 m_aRepeatDelta(0),
73 m_pNext(nullptr)
77 Timer::Timer(const TTimeValue& rTime)
78 : m_aTimeOut(rTime),
79 m_aExpired(0),
80 m_aRepeatDelta(0),
81 m_pNext(nullptr)
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)
93 Timer::~Timer()
95 stop();
98 void Timer::start()
100 if (!isTicking())
102 if (!m_aTimeOut.isEmpty())
103 setRemainingTime(m_aTimeOut);
105 TimerManager *pManager = TimerManager::getTimerManager();
107 if (pManager)
108 pManager->registerTimer(this);
112 void Timer::stop()
114 TimerManager *pManager = TimerManager::getTimerManager();
116 if (pManager)
117 pManager->unregisterTimer(this);
120 sal_Bool Timer::isTicking() const
122 TimerManager *pManager = TimerManager::getTimerManager();
124 if (pManager)
125 return pManager->lookupTimer(this);
126 else
127 return false;
130 sal_Bool Timer::isExpired() const
132 TTimeValue Now;
134 osl_getSystemTime(&Now);
136 return !(Now < m_aExpired);
139 sal_Bool Timer::expiresBefore(const Timer* pTimer) const
141 if (pTimer)
142 return m_aExpired < pTimer->m_aExpired;
143 else
144 return false;
147 void Timer::setAbsoluteTime(const TTimeValue& Time)
149 m_aTimeOut = 0;
150 m_aExpired = Time;
151 m_aRepeatDelta = 0;
153 m_aExpired.normalize();
156 void Timer::setRemainingTime(const TTimeValue& Remaining)
158 osl_getSystemTime(&m_aExpired);
160 m_aExpired.addTime(Remaining);
163 void Timer::setRemainingTime(const TTimeValue& Remaining, const TTimeValue& Repeat)
165 osl_getSystemTime(&m_aExpired);
167 m_aExpired.addTime(Remaining);
169 m_aRepeatDelta = Repeat;
172 void Timer::addTime(const TTimeValue& Delta)
174 m_aExpired.addTime(Delta);
177 TTimeValue Timer::getRemainingTime() const
179 TTimeValue Now;
181 osl_getSystemTime(&Now);
183 sal_Int32 secs = m_aExpired.Seconds - Now.Seconds;
185 if (secs < 0)
186 return TTimeValue(0, 0);
188 sal_Int32 nsecs = m_aExpired.Nanosec - Now.Nanosec;
190 if (nsecs < 0)
192 if (secs > 0)
194 secs -= 1;
195 nsecs += 1000000000;
197 else
198 return TTimeValue(0, 0);
201 return TTimeValue(secs, nsecs);
204 namespace
206 // Synchronize access to TimerManager
207 struct theTimerManagerMutex : public rtl::Static< osl::Mutex, theTimerManagerMutex> {};
210 TimerManager* salhelper::TimerManager::m_pManager = nullptr;
212 /** The timer manager cleanup has been removed (no thread is killed anymore),
213 so the thread leaks.
215 This will result in a GPF in case the salhelper-library gets unloaded before
216 process termination.
218 @TODO : rewrite this file, so that the timerManager thread gets destroyed,
219 when there are no timers anymore !
222 TimerManager::TimerManager()
224 osl::MutexGuard Guard(theTimerManagerMutex::get());
226 assert(m_pManager == nullptr);
228 m_pManager = this;
229 m_pHead= nullptr;
230 m_notEmpty.reset();
232 // start thread
233 create();
236 TimerManager::~TimerManager()
238 osl::MutexGuard Guard(theTimerManagerMutex::get());
240 if (m_pManager == this)
241 m_pManager = nullptr;
244 void TimerManager::onTerminated()
246 delete this; // FIXME
249 TimerManager* TimerManager::getTimerManager()
251 osl::MutexGuard Guard(theTimerManagerMutex::get());
253 if (! m_pManager)
254 new TimerManager;
256 return m_pManager;
259 void TimerManager::registerTimer(Timer* pTimer)
261 if (!pTimer)
262 return;
264 osl::MutexGuard Guard(m_Lock);
266 // try to find one with equal or lower remaining time.
267 Timer** ppIter = &m_pHead;
269 while (*ppIter)
271 if (pTimer->expiresBefore(*ppIter))
273 // next element has higher remaining time,
274 // => insert new timer before
275 break;
277 ppIter= &((*ppIter)->m_pNext);
280 // next element has higher remaining time,
281 // => insert new timer before
282 pTimer->m_pNext= *ppIter;
283 *ppIter = pTimer;
286 if (pTimer == m_pHead)
288 // it was inserted as new head
289 // signal it to TimerManager Thread
290 m_notEmpty.set();
294 void TimerManager::unregisterTimer(Timer const * pTimer)
296 if (!pTimer)
297 return;
299 // lock access
300 osl::MutexGuard Guard(m_Lock);
302 Timer** ppIter = &m_pHead;
304 while (*ppIter)
306 if (pTimer == (*ppIter))
308 // remove timer from list
309 *ppIter = (*ppIter)->m_pNext;
310 return;
312 ppIter= &((*ppIter)->m_pNext);
316 bool TimerManager::lookupTimer(const Timer* pTimer)
318 if (!pTimer)
319 return false;
321 // lock access
322 osl::MutexGuard Guard(m_Lock);
324 // check the list
325 for (Timer* pIter = m_pHead; pIter != nullptr; pIter= pIter->m_pNext)
327 if (pIter == pTimer)
328 return true;
331 return false;
334 void TimerManager::checkForTimeout()
336 m_Lock.acquire();
338 if (!m_pHead)
340 m_Lock.release();
341 return;
344 Timer* pTimer = m_pHead;
346 if (pTimer->isExpired())
348 // remove expired timer
349 m_pHead = pTimer->m_pNext;
351 pTimer->acquire();
353 m_Lock.release();
355 pTimer->onShot();
357 // restart timer if specified
358 if (!pTimer->m_aRepeatDelta.isEmpty())
360 TTimeValue Now;
362 osl_getSystemTime(&Now);
364 Now.Seconds += pTimer->m_aRepeatDelta.Seconds;
365 Now.Nanosec += pTimer->m_aRepeatDelta.Nanosec;
367 pTimer->m_aExpired = Now;
369 registerTimer(pTimer);
371 pTimer->release();
373 else
375 m_Lock.release();
379 void TimerManager::run()
381 osl_setThreadName("salhelper::TimerManager");
383 setPriority( osl_Thread_PriorityBelowNormal );
385 while (schedule())
387 TTimeValue delay;
388 TTimeValue* pDelay=nullptr;
390 m_Lock.acquire();
392 if (m_pHead != nullptr)
394 delay = m_pHead->getRemainingTime();
395 pDelay=&delay;
397 else
399 pDelay=nullptr;
403 m_notEmpty.reset();
405 m_Lock.release();
408 m_notEmpty.wait(pDelay);
410 checkForTimeout();
415 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */