| /************************************************************** |
| * |
| * 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. |
| * |
| *************************************************************/ |
| |
| |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_sdext.hxx" |
| |
| #include "PresenterTimer.hxx" |
| #include <osl/doublecheckedlocking.h> |
| #include <osl/thread.hxx> |
| #include <boost/bind.hpp> |
| #include <boost/function.hpp> |
| #include <boost/enable_shared_from_this.hpp> |
| #include <set> |
| |
| using namespace ::com::sun::star; |
| using namespace ::com::sun::star::uno; |
| |
| #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString))) |
| |
| namespace sdext { namespace presenter { |
| |
| namespace { |
| class TimerTask |
| { |
| public: |
| TimerTask ( |
| const PresenterTimer::Task& rTask, |
| const TimeValue& rDueTime, |
| const sal_Int64 nRepeatIntervall, |
| const sal_Int32 nTaskId); |
| ~TimerTask (void) {} |
| |
| PresenterTimer::Task maTask; |
| TimeValue maDueTime; |
| const sal_Int64 mnRepeatIntervall; |
| const sal_Int32 mnTaskId; |
| bool mbIsCanceled; |
| }; |
| |
| typedef ::boost::shared_ptr<TimerTask> SharedTimerTask; |
| |
| |
| class TimerTaskComparator |
| { |
| public: |
| bool operator() (const SharedTimerTask& rpTask1, const SharedTimerTask& rpTask2) |
| { |
| return rpTask1->maDueTime.Seconds < rpTask2->maDueTime.Seconds |
| || (rpTask1->maDueTime.Seconds == rpTask2->maDueTime.Seconds |
| && rpTask1->maDueTime.Nanosec < rpTask2->maDueTime.Nanosec); |
| } |
| }; |
| |
| |
| |
| |
| /** Queue all scheduled tasks and process them when their time has come. |
| */ |
| class TimerScheduler |
| : public ::boost::enable_shared_from_this<TimerScheduler>, |
| public ::osl::Thread |
| { |
| public: |
| static ::boost::shared_ptr<TimerScheduler> Instance (void); |
| static SharedTimerTask CreateTimerTask ( |
| const PresenterTimer::Task& rTask, |
| const TimeValue& rDueTime, |
| const sal_Int64 nRepeatIntervall); |
| |
| void ScheduleTask (const SharedTimerTask& rpTask); |
| void CancelTask (const sal_Int32 nTaskId); |
| |
| static bool GetCurrentTime (TimeValue& rCurrentTime); |
| static sal_Int64 GetTimeDifference ( |
| const TimeValue& rTargetTime, |
| const TimeValue& rCurrentTime); |
| static void ConvertToTimeValue ( |
| TimeValue& rTimeValue, |
| const sal_Int64 nTimeDifference); |
| static sal_Int64 ConvertFromTimeValue ( |
| const TimeValue& rTimeValue); |
| |
| private: |
| static ::boost::shared_ptr<TimerScheduler> mpInstance; |
| static ::osl::Mutex maInstanceMutex; |
| static sal_Int32 mnTaskId; |
| |
| ::osl::Mutex maTaskContainerMutex; |
| typedef ::std::set<SharedTimerTask,TimerTaskComparator> TaskContainer; |
| TaskContainer maScheduledTasks; |
| bool mbIsRunning; |
| ::osl::Mutex maCurrentTaskMutex; |
| SharedTimerTask mpCurrentTask; |
| |
| static void Release (void); |
| |
| TimerScheduler (void); |
| virtual ~TimerScheduler (void); |
| class Deleter {public: void operator () (TimerScheduler* pScheduler) { delete pScheduler; } }; |
| friend class Deleter; |
| |
| virtual void SAL_CALL run (void); |
| virtual void SAL_CALL onTerminated (void); |
| }; |
| |
| |
| |
| |
| bool GetDateTime (oslDateTime& rDateTime); |
| } // end of anonymous namespace |
| |
| |
| //===== PresenterTimer ======================================================== |
| |
| sal_Int32 PresenterTimer::ScheduleSingleTaskRelative ( |
| const Task& rTask, |
| const sal_Int64 nDelay) |
| { |
| return ScheduleRepeatedTask(rTask, nDelay, 0); |
| } |
| |
| |
| |
| |
| sal_Int32 PresenterTimer::ScheduleSingleTaskAbsolute ( |
| const Task& rTask, |
| const TimeValue& rDueTime) |
| { |
| SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, rDueTime, 0)); |
| TimerScheduler::Instance()->ScheduleTask(pTask); |
| return pTask->mnTaskId; |
| } |
| |
| |
| |
| |
| sal_Int32 PresenterTimer::ScheduleRepeatedTask ( |
| const Task& rTask, |
| const sal_Int64 nDelay, |
| const sal_Int64 nIntervall) |
| { |
| TimeValue aCurrentTime; |
| if (TimerScheduler::GetCurrentTime(aCurrentTime)) |
| { |
| TimeValue aDueTime; |
| TimerScheduler::ConvertToTimeValue( |
| aDueTime, |
| TimerScheduler::ConvertFromTimeValue (aCurrentTime) + nDelay); |
| SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, aDueTime, nIntervall)); |
| TimerScheduler::Instance()->ScheduleTask(pTask); |
| return pTask->mnTaskId; |
| } |
| |
| return NotAValidTaskId; |
| } |
| |
| |
| |
| |
| void PresenterTimer::CancelTask (const sal_Int32 nTaskId) |
| { |
| return TimerScheduler::Instance()->CancelTask(nTaskId); |
| } |
| |
| |
| |
| |
| //===== TimerScheduler ======================================================== |
| |
| ::boost::shared_ptr<TimerScheduler> TimerScheduler::mpInstance; |
| ::osl::Mutex TimerScheduler::maInstanceMutex; |
| sal_Int32 TimerScheduler::mnTaskId = PresenterTimer::NotAValidTaskId; |
| |
| ::boost::shared_ptr<TimerScheduler> TimerScheduler::Instance (void) |
| { |
| ::boost::shared_ptr<TimerScheduler> pInstance = mpInstance; |
| if (pInstance.get() == NULL) |
| { |
| ::osl::MutexGuard aGuard (maInstanceMutex); |
| pInstance = mpInstance; |
| if (pInstance.get() == NULL) |
| { |
| pInstance.reset(new TimerScheduler(), TimerScheduler::Deleter()); |
| OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); |
| mpInstance = pInstance; |
| } |
| } |
| else |
| { |
| OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); |
| } |
| return pInstance; |
| } |
| |
| |
| |
| |
| void TimerScheduler::Release (void) |
| { |
| ::osl::MutexGuard aGuard (maInstanceMutex); |
| mpInstance.reset(); |
| } |
| |
| |
| |
| |
| TimerScheduler::TimerScheduler (void) |
| : maTaskContainerMutex(), |
| maScheduledTasks(), |
| mbIsRunning(false), |
| maCurrentTaskMutex(), |
| mpCurrentTask() |
| { |
| } |
| |
| |
| |
| |
| TimerScheduler::~TimerScheduler (void) |
| { |
| } |
| |
| |
| |
| SharedTimerTask TimerScheduler::CreateTimerTask ( |
| const PresenterTimer::Task& rTask, |
| const TimeValue& rDueTime, |
| const sal_Int64 nRepeatIntervall) |
| { |
| return SharedTimerTask(new TimerTask(rTask, rDueTime, nRepeatIntervall, ++mnTaskId)); |
| } |
| |
| |
| |
| |
| void TimerScheduler::ScheduleTask (const SharedTimerTask& rpTask) |
| { |
| if (rpTask.get() == NULL) |
| return; |
| if (rpTask->mbIsCanceled) |
| return; |
| |
| osl::MutexGuard aGuard (maTaskContainerMutex); |
| maScheduledTasks.insert(rpTask); |
| |
| if ( ! mbIsRunning) |
| { |
| mbIsRunning = true; |
| create(); |
| } |
| } |
| |
| |
| |
| |
| void TimerScheduler::CancelTask (const sal_Int32 nTaskId) |
| { |
| // Set of scheduled tasks is sorted after their due times, not their |
| // task ids. Therefore we have to do a linear search for the task to |
| // cancel. |
| { |
| ::osl::MutexGuard aGuard (maTaskContainerMutex); |
| TaskContainer::iterator iTask (maScheduledTasks.begin()); |
| TaskContainer::const_iterator iEnd (maScheduledTasks.end()); |
| for ( ; iTask!=iEnd; ++iTask) |
| { |
| if ((*iTask)->mnTaskId == nTaskId) |
| { |
| maScheduledTasks.erase(iTask); |
| break; |
| } |
| } |
| } |
| |
| // The task that is to be canceled may be currently about to be |
| // processed. Mark it with a flag that a) prevents a repeating task |
| // from being scheduled again and b) tries to prevent its execution. |
| if (mpCurrentTask.get() != NULL |
| && mpCurrentTask->mnTaskId == nTaskId) |
| { |
| mpCurrentTask->mbIsCanceled = true; |
| } |
| |
| // When the last active task was canceled then the timer can be |
| // stopped. |
| if (maScheduledTasks.size() == 0) |
| { |
| mbIsRunning = false; |
| resume(); |
| // join(); |
| } |
| } |
| |
| |
| |
| |
| void SAL_CALL TimerScheduler::run (void) |
| { |
| while (mbIsRunning) |
| { |
| // Get the current time. |
| TimeValue aCurrentTime; |
| if ( ! GetCurrentTime(aCurrentTime)) |
| { |
| // We can not get the current time and thus can not schedule anything. |
| break; |
| } |
| |
| // Restrict access to the maScheduledTasks member to one, mutext |
| // guarded, block. |
| SharedTimerTask pTask; |
| sal_Int64 nDifference = 0; |
| { |
| ::osl::MutexGuard aGuard (maTaskContainerMutex); |
| |
| // There are no more scheduled task. Leave this loop, function and |
| // live of the TimerScheduler. |
| if (maScheduledTasks.empty()) |
| break; |
| |
| nDifference = GetTimeDifference( |
| (*maScheduledTasks.begin())->maDueTime, |
| aCurrentTime); |
| if (nDifference <= 0) |
| { |
| pTask = *maScheduledTasks.begin(); |
| maScheduledTasks.erase(maScheduledTasks.begin()); |
| } |
| } |
| |
| // Acquire a reference to the current task. |
| { |
| ::osl::MutexGuard aGuard (maCurrentTaskMutex); |
| mpCurrentTask = pTask; |
| } |
| |
| if (mpCurrentTask.get() == NULL) |
| { |
| // Wait until the first task becomes due. |
| TimeValue aTimeValue; |
| ConvertToTimeValue(aTimeValue, nDifference); |
| wait(aTimeValue); |
| } |
| else |
| { |
| // Execute task. |
| if ( ! mpCurrentTask->maTask.empty() |
| && ! mpCurrentTask->mbIsCanceled) |
| { |
| mpCurrentTask->maTask(aCurrentTime); |
| |
| // Re-schedule repeating tasks. |
| if (mpCurrentTask->mnRepeatIntervall > 0) |
| { |
| ConvertToTimeValue( |
| mpCurrentTask->maDueTime, |
| ConvertFromTimeValue(mpCurrentTask->maDueTime) |
| + mpCurrentTask->mnRepeatIntervall); |
| ScheduleTask(mpCurrentTask); |
| } |
| } |
| |
| } |
| |
| // Release reference to the current task. |
| { |
| ::osl::MutexGuard aGuard (maCurrentTaskMutex); |
| mpCurrentTask.reset(); |
| } |
| } |
| } |
| |
| |
| |
| |
| void SAL_CALL TimerScheduler::onTerminated (void) |
| { |
| Release(); |
| } |
| |
| |
| |
| |
| bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime) |
| { |
| TimeValue aSystemTime; |
| if (osl_getSystemTime(&aSystemTime)) |
| return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime); |
| return false; |
| } |
| |
| |
| |
| |
| sal_Int64 TimerScheduler::GetTimeDifference ( |
| const TimeValue& rTargetTime, |
| const TimeValue& rCurrentTime) |
| { |
| return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime); |
| } |
| |
| |
| |
| |
| void TimerScheduler::ConvertToTimeValue ( |
| TimeValue& rTimeValue, |
| const sal_Int64 nTimeDifference) |
| { |
| rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L); |
| rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L); |
| } |
| |
| |
| |
| |
| sal_Int64 TimerScheduler::ConvertFromTimeValue ( |
| const TimeValue& rTimeValue) |
| { |
| return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec; |
| } |
| |
| |
| |
| |
| //===== TimerTask ============================================================= |
| |
| namespace { |
| |
| TimerTask::TimerTask ( |
| const PresenterTimer::Task& rTask, |
| const TimeValue& rDueTime, |
| const sal_Int64 nRepeatIntervall, |
| const sal_Int32 nTaskId) |
| : maTask(rTask), |
| maDueTime(rDueTime), |
| mnRepeatIntervall(nRepeatIntervall), |
| mnTaskId(nTaskId), |
| mbIsCanceled(false) |
| { |
| } |
| |
| } // end of anonymous namespace |
| |
| |
| |
| |
| //===== PresenterTimer ======================================================== |
| |
| |
| ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::mpInstance; |
| |
| ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::Instance ( |
| const css::uno::Reference<css::uno::XComponentContext>& rxContext) |
| { |
| ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex()); |
| |
| ::rtl::Reference<PresenterClockTimer> pTimer; |
| if (mpInstance.is()) |
| { |
| pTimer = mpInstance; |
| } |
| if ( ! pTimer.is()) |
| { |
| pTimer = ::rtl::Reference<PresenterClockTimer>(new PresenterClockTimer(rxContext)); |
| mpInstance = pTimer; |
| } |
| return pTimer; |
| } |
| |
| |
| |
| |
| PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext) |
| : PresenterClockTimerInterfaceBase(m_aMutex), |
| maListeners(), |
| maDateTime(), |
| mnTimerTaskId(PresenterTimer::NotAValidTaskId), |
| mbIsCallbackPending(false), |
| mxRequestCallback() |
| { |
| Reference<lang::XMultiComponentFactory> xFactory ( |
| rxContext->getServiceManager(), UNO_QUERY); |
| if (xFactory.is()) |
| mxRequestCallback = Reference<awt::XRequestCallback>( |
| xFactory->createInstanceWithContext( |
| A2S("com.sun.star.awt.AsyncCallback"), |
| rxContext), |
| UNO_QUERY_THROW); |
| } |
| |
| |
| |
| |
| PresenterClockTimer::~PresenterClockTimer (void) |
| { |
| if (mnTimerTaskId != PresenterTimer::NotAValidTaskId) |
| { |
| PresenterTimer::CancelTask(mnTimerTaskId); |
| mnTimerTaskId = PresenterTimer::NotAValidTaskId; |
| } |
| |
| Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY); |
| if (xComponent.is()) |
| xComponent->dispose(); |
| mxRequestCallback = NULL; |
| } |
| |
| |
| |
| |
| void PresenterClockTimer::AddListener (const SharedListener& rListener) |
| { |
| osl::MutexGuard aGuard (maMutex); |
| |
| maListeners.push_back(rListener); |
| |
| // Create a timer task when the first listener is added. |
| if (mnTimerTaskId==PresenterTimer::NotAValidTaskId) |
| { |
| mnTimerTaskId = PresenterTimer::ScheduleRepeatedTask( |
| ::boost::bind(&PresenterClockTimer::CheckCurrentTime, this, _1), |
| 0, |
| 250000000 /*ns*/); |
| } |
| } |
| |
| |
| |
| |
| void PresenterClockTimer::RemoveListener (const SharedListener& rListener) |
| { |
| osl::MutexGuard aGuard (maMutex); |
| |
| ListenerContainer::iterator iListener (::std::find( |
| maListeners.begin(), |
| maListeners.end(), |
| rListener)); |
| if (iListener != maListeners.end()) |
| maListeners.erase(iListener); |
| if (maListeners.size() == 0) |
| { |
| // We have no more clients and therefore are not interested in time changes. |
| if (mnTimerTaskId != PresenterTimer::NotAValidTaskId) |
| { |
| PresenterTimer::CancelTask(mnTimerTaskId); |
| mnTimerTaskId = PresenterTimer::NotAValidTaskId; |
| } |
| mpInstance = NULL; |
| } |
| } |
| |
| |
| |
| |
| oslDateTime PresenterClockTimer::GetCurrentTime (void) |
| { |
| TimeValue aCurrentTime; |
| TimerScheduler::GetCurrentTime(aCurrentTime); |
| oslDateTime aDateTime; |
| osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime); |
| return aDateTime; |
| } |
| |
| |
| |
| |
| sal_Int64 PresenterClockTimer::GetTimeDifference ( |
| const oslDateTime& rNow, |
| const oslDateTime& rThen) |
| { |
| TimeValue aNow; |
| TimeValue aThen; |
| if (osl_getTimeValueFromDateTime(const_cast<oslDateTime*>(&rNow),&aNow) |
| && osl_getTimeValueFromDateTime(const_cast<oslDateTime*>(&rThen),&aThen)) |
| { |
| return TimerScheduler::GetTimeDifference(aNow, aThen); |
| } |
| else |
| return -1; |
| } |
| |
| |
| |
| |
| void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime) |
| { |
| css::uno::Reference<css::awt::XRequestCallback> xRequestCallback; |
| css::uno::Reference<css::awt::XCallback> xCallback; |
| { |
| osl::MutexGuard aGuard (maMutex); |
| |
| TimeValue aCurrentTime (rCurrentTime); |
| oslDateTime aDateTime; |
| if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime)) |
| { |
| if (aDateTime.Seconds != maDateTime.Seconds |
| || aDateTime.Minutes != maDateTime.Minutes |
| || aDateTime.Seconds != maDateTime.Seconds) |
| { |
| // The displayed part of the current time has changed. |
| // Prepare to call the listeners. |
| maDateTime = aDateTime; |
| |
| // Schedule notification of listeners. |
| if (mxRequestCallback.is() && ! mbIsCallbackPending) |
| { |
| mbIsCallbackPending = true; |
| xRequestCallback = mxRequestCallback; |
| xCallback = this; |
| } |
| } |
| } |
| } |
| if (mxRequestCallback.is() && xCallback.is()) |
| xRequestCallback->addCallback(xCallback, Any()); |
| } |
| |
| |
| |
| |
| //----- XCallback ------------------------------------------------------------- |
| |
| void SAL_CALL PresenterClockTimer::notify (const css::uno::Any& rUserData) |
| throw (css::uno::RuntimeException) |
| { |
| (void)rUserData; |
| |
| ListenerContainer aListenerCopy (maListeners); |
| |
| { |
| osl::MutexGuard aGuard (maMutex); |
| |
| mbIsCallbackPending = false; |
| |
| ::std::copy( |
| maListeners.begin(), |
| maListeners.end(), |
| ::std::back_inserter(aListenerCopy)); |
| } |
| |
| if (aListenerCopy.size() > 0) |
| { |
| ListenerContainer::const_iterator iListener; |
| ListenerContainer::const_iterator iEnd (aListenerCopy.end()); |
| for (iListener=aListenerCopy.begin(); iListener!=iEnd; ++iListener) |
| { |
| (*iListener)->TimeHasChanged(maDateTime); |
| } |
| } |
| } |
| |
| |
| |
| } } // end of namespace ::sdext::presenter |