Bump version to 24.04.3.4
[LibreOffice.git] / salhelper / source / timer.cxx
blob2af5b6bfdd90455e873959019046525347253e28
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>
23 #include <condition_variable>
24 #include <mutex>
26 using namespace salhelper;
28 class salhelper::TimerManager final : public osl::Thread
30 public:
31 TimerManager();
33 ~TimerManager();
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 protected:
45 /// worker-function of thread
46 virtual void SAL_CALL run() override;
48 /// Checking and triggering of a timer event
49 void checkForTimeout();
51 /// sorted-queue data
52 salhelper::Timer* m_pHead;
53 bool m_terminate;
54 /// List Protection
55 std::mutex m_Lock;
56 /// Signal the insertion of a timer
57 std::condition_variable m_notEmpty;
59 /// "Singleton Pattern"
60 //static salhelper::TimerManager* m_pManager;
64 namespace
66 salhelper::TimerManager& getTimerManager()
68 static salhelper::TimerManager aManager;
69 return aManager;
74 Timer::Timer()
75 : m_aTimeOut(0),
76 m_aExpired(0),
77 m_aRepeatDelta(0),
78 m_pNext(nullptr)
82 Timer::Timer(const TTimeValue& rTime)
83 : m_aTimeOut(rTime),
84 m_aExpired(0),
85 m_aRepeatDelta(0),
86 m_pNext(nullptr)
90 Timer::Timer(const TTimeValue& rTime, const TTimeValue& Repeat)
91 : m_aTimeOut(rTime),
92 m_aExpired(0),
93 m_aRepeatDelta(Repeat),
94 m_pNext(nullptr)
98 Timer::~Timer()
100 stop();
103 void Timer::start()
105 if (!isTicking())
107 if (!m_aTimeOut.isEmpty())
108 setRemainingTime(m_aTimeOut);
110 getTimerManager().registerTimer(this);
114 void Timer::stop()
116 getTimerManager().unregisterTimer(this);
119 sal_Bool Timer::isTicking() const
121 return getTimerManager().lookupTimer(this);
124 sal_Bool Timer::isExpired() const
126 TTimeValue Now;
128 osl_getSystemTime(&Now);
130 return !(Now < m_aExpired);
133 sal_Bool Timer::expiresBefore(const Timer* pTimer) const
135 if (pTimer)
136 return m_aExpired < pTimer->m_aExpired;
137 else
138 return false;
141 void Timer::setAbsoluteTime(const TTimeValue& Time)
143 m_aTimeOut = 0;
144 m_aExpired = Time;
145 m_aRepeatDelta = 0;
147 m_aExpired.normalize();
150 void Timer::setRemainingTime(const TTimeValue& Remaining)
152 osl_getSystemTime(&m_aExpired);
154 m_aExpired.addTime(Remaining);
157 void Timer::setRemainingTime(const TTimeValue& Remaining, const TTimeValue& Repeat)
159 osl_getSystemTime(&m_aExpired);
161 m_aExpired.addTime(Remaining);
163 m_aRepeatDelta = Repeat;
166 void Timer::addTime(const TTimeValue& Delta)
168 m_aExpired.addTime(Delta);
171 TTimeValue Timer::getRemainingTime() const
173 TTimeValue Now;
175 osl_getSystemTime(&Now);
177 sal_Int32 secs = m_aExpired.Seconds - Now.Seconds;
179 if (secs < 0)
180 return TTimeValue(0, 0);
182 sal_Int32 nsecs = m_aExpired.Nanosec - Now.Nanosec;
184 if (nsecs < 0)
186 if (secs > 0)
188 secs -= 1;
189 nsecs += 1000000000;
191 else
192 return TTimeValue(0, 0);
195 return TTimeValue(secs, nsecs);
198 /** The timer manager cleanup has been removed (no thread is killed anymore),
199 so the thread leaks.
201 This will result in a GPF in case the salhelper-library gets unloaded before
202 process termination.
204 @TODO : rewrite this file, so that the timerManager thread gets destroyed,
205 when there are no timers anymore !
208 TimerManager::TimerManager() :
209 m_pHead(nullptr), m_terminate(false)
211 // start thread
212 create();
215 TimerManager::~TimerManager() {
217 std::scoped_lock g(m_Lock);
218 m_terminate = true;
220 m_notEmpty.notify_all();
221 join();
224 void TimerManager::registerTimer(Timer* pTimer)
226 if (!pTimer)
227 return;
229 bool notify = false;
231 std::lock_guard Guard(m_Lock);
233 // try to find one with equal or lower remaining time.
234 Timer** ppIter = &m_pHead;
236 while (*ppIter)
238 if (pTimer->expiresBefore(*ppIter))
240 // next element has higher remaining time,
241 // => insert new timer before
242 break;
244 ppIter= &((*ppIter)->m_pNext);
247 // next element has higher remaining time,
248 // => insert new timer before
249 pTimer->m_pNext= *ppIter;
250 *ppIter = pTimer;
253 if (pTimer == m_pHead)
255 notify = true;
259 if (notify) {
260 // it was inserted as new head
261 // signal it to TimerManager Thread
262 m_notEmpty.notify_all();
266 void TimerManager::unregisterTimer(Timer const * pTimer)
268 if (!pTimer)
269 return;
271 // lock access
272 std::lock_guard Guard(m_Lock);
274 Timer** ppIter = &m_pHead;
276 while (*ppIter)
278 if (pTimer == (*ppIter))
280 // remove timer from list
281 *ppIter = (*ppIter)->m_pNext;
282 return;
284 ppIter= &((*ppIter)->m_pNext);
288 bool TimerManager::lookupTimer(const Timer* pTimer)
290 if (!pTimer)
291 return false;
293 // lock access
294 std::lock_guard Guard(m_Lock);
296 // check the list
297 for (Timer* pIter = m_pHead; pIter != nullptr; pIter= pIter->m_pNext)
299 if (pIter == pTimer)
300 return true;
303 return false;
306 void TimerManager::checkForTimeout()
308 std::unique_lock aLock (m_Lock);
310 if (!m_pHead)
312 return;
315 Timer* pTimer = m_pHead;
317 if (!pTimer->isExpired())
318 return;
320 // remove expired timer
321 m_pHead = pTimer->m_pNext;
323 pTimer->acquire();
325 aLock.unlock();
327 pTimer->onShot();
329 // restart timer if specified
330 if (!pTimer->m_aRepeatDelta.isEmpty())
332 TTimeValue Now;
334 osl_getSystemTime(&Now);
336 Now.Seconds += pTimer->m_aRepeatDelta.Seconds;
337 Now.Nanosec += pTimer->m_aRepeatDelta.Nanosec;
339 pTimer->m_aExpired = Now;
341 registerTimer(pTimer);
343 pTimer->release();
346 void TimerManager::run()
348 osl_setThreadName("salhelper::TimerManager");
350 setPriority( osl_Thread_PriorityBelowNormal );
352 while (schedule())
355 std::unique_lock a_Guard(m_Lock);
357 if (m_pHead != nullptr)
359 TTimeValue delay = m_pHead->getRemainingTime();
360 m_notEmpty.wait_for(
361 a_Guard,
362 std::chrono::nanoseconds(
363 sal_Int64(delay.Seconds) * 1'000'000'000 + delay.Nanosec),
364 [this] { return m_terminate; });
366 else
368 m_notEmpty.wait(a_Guard, [this] { return m_terminate || m_pHead != nullptr; });
371 if (m_terminate) {
372 break;
376 checkForTimeout();
381 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */