1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
26 using namespace salhelper
;
28 class salhelper::TimerManager final
: public osl::Thread
36 void registerTimer(salhelper::Timer
* pTimer
);
39 void unregisterTimer(salhelper::Timer
const * pTimer
);
42 bool lookupTimer(const salhelper::Timer
* pTimer
);
45 /// worker-function of thread
46 virtual void SAL_CALL
run() override
;
48 /// Checking and triggering of a timer event
49 void checkForTimeout();
52 salhelper::Timer
* m_pHead
;
56 /// Signal the insertion of a timer
57 std::condition_variable m_notEmpty
;
59 /// "Singleton Pattern"
60 //static salhelper::TimerManager* m_pManager;
66 salhelper::TimerManager
& getTimerManager()
68 static salhelper::TimerManager aManager
;
82 Timer::Timer(const TTimeValue
& rTime
)
90 Timer::Timer(const TTimeValue
& rTime
, const TTimeValue
& Repeat
)
93 m_aRepeatDelta(Repeat
),
107 if (!m_aTimeOut
.isEmpty())
108 setRemainingTime(m_aTimeOut
);
110 getTimerManager().registerTimer(this);
116 getTimerManager().unregisterTimer(this);
119 sal_Bool
Timer::isTicking() const
121 return getTimerManager().lookupTimer(this);
124 sal_Bool
Timer::isExpired() const
128 osl_getSystemTime(&Now
);
130 return !(Now
< m_aExpired
);
133 sal_Bool
Timer::expiresBefore(const Timer
* pTimer
) const
136 return m_aExpired
< pTimer
->m_aExpired
;
141 void Timer::setAbsoluteTime(const TTimeValue
& Time
)
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
175 osl_getSystemTime(&Now
);
177 sal_Int32 secs
= m_aExpired
.Seconds
- Now
.Seconds
;
180 return TTimeValue(0, 0);
182 sal_Int32 nsecs
= m_aExpired
.Nanosec
- Now
.Nanosec
;
192 return TTimeValue(0, 0);
195 return TTimeValue(secs
, nsecs
);
198 /** The timer manager cleanup has been removed (no thread is killed anymore),
201 This will result in a GPF in case the salhelper-library gets unloaded before
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)
215 TimerManager::~TimerManager() {
217 std::scoped_lock
g(m_Lock
);
220 m_notEmpty
.notify_all();
224 void TimerManager::registerTimer(Timer
* pTimer
)
231 std::lock_guard
Guard(m_Lock
);
233 // try to find one with equal or lower remaining time.
234 Timer
** ppIter
= &m_pHead
;
238 if (pTimer
->expiresBefore(*ppIter
))
240 // next element has higher remaining time,
241 // => insert new timer before
244 ppIter
= &((*ppIter
)->m_pNext
);
247 // next element has higher remaining time,
248 // => insert new timer before
249 pTimer
->m_pNext
= *ppIter
;
253 if (pTimer
== m_pHead
)
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
)
272 std::lock_guard
Guard(m_Lock
);
274 Timer
** ppIter
= &m_pHead
;
278 if (pTimer
== (*ppIter
))
280 // remove timer from list
281 *ppIter
= (*ppIter
)->m_pNext
;
284 ppIter
= &((*ppIter
)->m_pNext
);
288 bool TimerManager::lookupTimer(const Timer
* pTimer
)
294 std::lock_guard
Guard(m_Lock
);
297 for (Timer
* pIter
= m_pHead
; pIter
!= nullptr; pIter
= pIter
->m_pNext
)
306 void TimerManager::checkForTimeout()
308 std::unique_lock
aLock (m_Lock
);
315 Timer
* pTimer
= m_pHead
;
317 if (!pTimer
->isExpired())
320 // remove expired timer
321 m_pHead
= pTimer
->m_pNext
;
329 // restart timer if specified
330 if (!pTimer
->m_aRepeatDelta
.isEmpty())
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
);
346 void TimerManager::run()
348 osl_setThreadName("salhelper::TimerManager");
350 setPriority( osl_Thread_PriorityBelowNormal
);
355 std::unique_lock
a_Guard(m_Lock
);
357 if (m_pHead
!= nullptr)
359 TTimeValue delay
= m_pHead
->getRemainingTime();
362 std::chrono::nanoseconds(
363 sal_Int64(delay
.Seconds
) * 1'000'000'000 + delay
.Nanosec
),
364 [this] { return m_terminate
; });
368 m_notEmpty
.wait(a_Guard
, [this] { return m_terminate
|| m_pHead
!= nullptr; });
381 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */