/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ #include #include #include #include #include #include ///////////////////////////////////////////////////////////////////////////// // // Timer manager // class OTimerManagerCleanup; class vos::OTimerManager : public vos::OThread { public: /// OTimerManager(); /// ~OTimerManager(); /// register timer sal_Bool SAL_CALL registerTimer(vos::OTimer* pTimer); /// unregister timer sal_Bool SAL_CALL unregisterTimer(vos::OTimer* pTimer); /// lookup timer sal_Bool SAL_CALL lookupTimer(const vos::OTimer* pTimer); /// retrieves the "Singleton" TimerManager Instance static OTimerManager* SAL_CALL getTimerManager(); protected: /// worker-function of thread virtual void SAL_CALL run(); // Checking and triggering of a timer event void SAL_CALL checkForTimeout(); // cleanup Method virtual void SAL_CALL onTerminated(); // sorted-queue data vos::OTimer* m_pHead; // List Protection vos::OMutex m_Lock; // Signal the insertion of a timer vos::OCondition m_notEmpty; // Synchronize access to OTimerManager static vos::OMutex m_Access; // "Singleton Pattern" static vos::OTimerManager* m_pManager; friend class OTimerManagerCleanup; }; using namespace vos; ///////////////////////////////////////////////////////////////////////////// // // Timer class // VOS_IMPLEMENT_CLASSINFO(VOS_CLASSNAME(OTimer, vos), VOS_NAMESPACE(OTimer, vos), VOS_NAMESPACE(OObject, vos), 0); OTimer::OTimer() { m_TimeOut = 0; m_Expired = 0; m_RepeatDelta = 0; m_pNext = 0; } OTimer::OTimer(const TTimeValue& Time) { m_TimeOut = Time; m_RepeatDelta = 0; m_Expired = 0; m_pNext = 0; m_TimeOut.normalize(); } OTimer::OTimer(const TTimeValue& Time, const TTimeValue& Repeat) { m_TimeOut = Time; m_RepeatDelta = Repeat; m_Expired = 0; m_pNext = 0; m_TimeOut.normalize(); m_RepeatDelta.normalize(); } OTimer::~OTimer() { stop(); } void OTimer::start() { if (! isTicking()) { if (! m_TimeOut.isEmpty()) setRemainingTime(m_TimeOut); OTimerManager *pManager = OTimerManager::getTimerManager(); VOS_ASSERT(pManager); if ( pManager != 0 ) { pManager->registerTimer(this); } } } void OTimer::stop() { OTimerManager *pManager = OTimerManager::getTimerManager(); VOS_ASSERT(pManager); if ( pManager != 0 ) { pManager->unregisterTimer(this); } } sal_Bool OTimer::isTicking() const { OTimerManager *pManager = OTimerManager::getTimerManager(); VOS_ASSERT(pManager); if (pManager) return pManager->lookupTimer(this); else return sal_False; } sal_Bool OTimer::isExpired() const { TTimeValue Now; osl_getSystemTime(&Now); return !(Now < m_Expired); } sal_Bool OTimer::expiresBefore(const OTimer* pTimer) const { VOS_ASSERT(pTimer); if ( pTimer != 0 ) { return m_Expired < pTimer->m_Expired; } else { return sal_False; } } void OTimer::setAbsoluteTime(const TTimeValue& Time) { m_TimeOut = 0; m_Expired = Time; m_RepeatDelta = 0; m_Expired.normalize(); } void OTimer::setRemainingTime(const TTimeValue& Remaining) { osl_getSystemTime(&m_Expired); m_Expired.addTime(Remaining); } void OTimer::setRemainingTime(const TTimeValue& Remaining, const TTimeValue& Repeat) { osl_getSystemTime(&m_Expired); m_Expired.addTime(Remaining); m_RepeatDelta = Repeat; } void OTimer::addTime(const TTimeValue& Delta) { m_Expired.addTime(Delta); } TTimeValue OTimer::getRemainingTime() const { TTimeValue Now; osl_getSystemTime(&Now); sal_Int32 secs = m_Expired.Seconds - Now.Seconds; if (secs < 0) return TTimeValue(0, 0); sal_Int32 nsecs = m_Expired.Nanosec - Now.Nanosec; if (nsecs < 0) { if (secs > 0) { secs -= 1; nsecs += 1000000000; } else return TTimeValue(0, 0); } return TTimeValue(secs, nsecs); } ///////////////////////////////////////////////////////////////////////////// // // Timer manager // OMutex vos::OTimerManager::m_Access; OTimerManager* vos::OTimerManager::m_pManager=0; OTimerManager::OTimerManager() { OGuard Guard(&m_Access); VOS_ASSERT(m_pManager == 0); m_pManager = this; m_pHead= 0; m_notEmpty.reset(); // start thread create(); } OTimerManager::~OTimerManager() { OGuard Guard(&m_Access); if ( m_pManager == this ) m_pManager = 0; } void OTimerManager::onTerminated() { delete this; // mfe: AAARRRGGGHHH!!! } OTimerManager* OTimerManager::getTimerManager() { OGuard Guard(&m_Access); if (! m_pManager) new OTimerManager; return (m_pManager); } sal_Bool OTimerManager::registerTimer(OTimer* pTimer) { VOS_ASSERT(pTimer); if ( pTimer == 0 ) { return sal_False; } OGuard Guard(&m_Lock); // try to find one with equal or lower remaining time. OTimer** ppIter = &m_pHead; while (*ppIter) { if (pTimer->expiresBefore(*ppIter)) { // next element has higher remaining time, // => insert new timer before break; } ppIter= &((*ppIter)->m_pNext); } // next element has higher remaining time, // => insert new timer before pTimer->m_pNext= *ppIter; *ppIter = pTimer; if (pTimer == m_pHead) { // it was inserted as new head // signal it to TimerManager Thread m_notEmpty.set(); } return sal_True; } sal_Bool OTimerManager::unregisterTimer(OTimer* pTimer) { VOS_ASSERT(pTimer); if ( pTimer == 0 ) { return sal_False; } // lock access OGuard Guard(&m_Lock); OTimer** ppIter = &m_pHead; while (*ppIter) { if (pTimer == (*ppIter)) { // remove timer from list *ppIter = (*ppIter)->m_pNext; return sal_True; } ppIter= &((*ppIter)->m_pNext); } return sal_False; } sal_Bool OTimerManager::lookupTimer(const OTimer* pTimer) { VOS_ASSERT(pTimer); if ( pTimer == 0 ) { return sal_False; } // lock access OGuard Guard(&m_Lock); // check the list for (OTimer* pIter = m_pHead; pIter != 0; pIter= pIter->m_pNext) { if (pIter == pTimer) { return sal_True; } } return sal_False; } void OTimerManager::checkForTimeout() { m_Lock.acquire(); if ( m_pHead == 0 ) { m_Lock.release(); return; } OTimer* pTimer = m_pHead; if (pTimer->isExpired()) { // remove expired timer m_pHead = pTimer->m_pNext; pTimer->acquire(); m_Lock.release(); pTimer->onShot(); // restart timer if specified if ( ! pTimer->m_RepeatDelta.isEmpty() ) { TTimeValue Now; osl_getSystemTime(&Now); Now.Seconds += pTimer->m_RepeatDelta.Seconds; Now.Nanosec += pTimer->m_RepeatDelta.Nanosec; pTimer->m_Expired = Now; registerTimer(pTimer); } pTimer->release(); } else { m_Lock.release(); } return; } void OTimerManager::run() { setPriority(TPriority_BelowNormal); while (schedule()) { TTimeValue delay; TTimeValue* pDelay=0; m_Lock.acquire(); if (m_pHead != 0) { delay = m_pHead->getRemainingTime(); pDelay=&delay; } else { pDelay=0; } m_notEmpty.reset(); m_Lock.release(); m_notEmpty.wait(pDelay); checkForTimeout(); } } ///////////////////////////////////////////////////////////////////////////// // // Timer manager cleanup // // jbu: // The timer manager cleanup has been removed (no thread is killed anymore). // So the thread leaks. // This will result in a GPF in case the vos-library gets unloaded before // process termination. // -> TODO : rewrite this file, so that the timerManager thread gets destroyed, // when there are no timers anymore !