LCOV - code coverage report
Current view: top level - sdext/source/presenter - PresenterTimer.cxx (source / functions) Hit Total Coverage
Test: commit 0e63ca4fde4e446f346e35849c756a30ca294aab Lines: 4 183 2.2 %
Date: 2014-04-11 Functions: 2 30 6.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
       2             : /*
       3             :  * This file is part of the LibreOffice project.
       4             :  *
       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/.
       8             :  *
       9             :  * This file incorporates work covered by the following license notice:
      10             :  *
      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 .
      18             :  */
      19             : 
      20             : #include "PresenterTimer.hxx"
      21             : #include <com/sun/star/lang/XMultiComponentFactory.hpp>
      22             : #include <com/sun/star/uno/XComponentContext.hpp>
      23             : #include <osl/doublecheckedlocking.h>
      24             : #include <osl/thread.hxx>
      25             : #include <boost/bind.hpp>
      26             : #include <boost/function.hpp>
      27             : #include <boost/enable_shared_from_this.hpp>
      28             : #include <set>
      29             : #include <iterator>
      30             : 
      31             : using namespace ::com::sun::star;
      32             : using namespace ::com::sun::star::uno;
      33             : 
      34             : namespace sdext { namespace presenter {
      35             : 
      36             : namespace {
      37             : class TimerTask
      38             : {
      39             : public:
      40             :     TimerTask (
      41             :         const PresenterTimer::Task& rTask,
      42             :         const TimeValue& rDueTime,
      43             :         const sal_Int64 nRepeatIntervall,
      44             :         const sal_Int32 nTaskId);
      45           0 :     ~TimerTask (void) {}
      46             : 
      47             :     PresenterTimer::Task maTask;
      48             :     TimeValue maDueTime;
      49             :     const sal_Int64 mnRepeatIntervall;
      50             :     const sal_Int32 mnTaskId;
      51             :     bool mbIsCanceled;
      52             : };
      53             : 
      54             : typedef ::boost::shared_ptr<TimerTask> SharedTimerTask;
      55             : 
      56             : class TimerTaskComparator
      57             : {
      58             : public:
      59           0 :     bool operator() (const SharedTimerTask& rpTask1, const SharedTimerTask& rpTask2) const
      60             :     {
      61           0 :         return rpTask1->maDueTime.Seconds < rpTask2->maDueTime.Seconds
      62           0 :             || (rpTask1->maDueTime.Seconds == rpTask2->maDueTime.Seconds
      63           0 :                 && rpTask1->maDueTime.Nanosec < rpTask2->maDueTime.Nanosec);
      64             :     }
      65             : };
      66             : 
      67             : /** Queue all scheduled tasks and process them when their time has come.
      68             : */
      69             : class TimerScheduler
      70             :     : public ::boost::enable_shared_from_this<TimerScheduler>,
      71             :       public ::osl::Thread
      72             : {
      73             : public:
      74             :     static ::boost::shared_ptr<TimerScheduler> Instance (void);
      75             :     static SharedTimerTask CreateTimerTask (
      76             :         const PresenterTimer::Task& rTask,
      77             :         const TimeValue& rDueTime,
      78             :         const sal_Int64 nRepeatIntervall);
      79             : 
      80             :     void ScheduleTask (const SharedTimerTask& rpTask);
      81             :     void CancelTask (const sal_Int32 nTaskId);
      82             : 
      83             :     static bool GetCurrentTime (TimeValue& rCurrentTime);
      84             :     static sal_Int64 GetTimeDifference (
      85             :         const TimeValue& rTargetTime,
      86             :         const TimeValue& rCurrentTime);
      87             :     static void ConvertToTimeValue (
      88             :         TimeValue& rTimeValue,
      89             :         const sal_Int64 nTimeDifference);
      90             :     static sal_Int64 ConvertFromTimeValue (
      91             :         const TimeValue& rTimeValue);
      92             : 
      93             : private:
      94             :     static ::boost::shared_ptr<TimerScheduler> mpInstance;
      95             :     static ::osl::Mutex maInstanceMutex;
      96             :     ::boost::shared_ptr<TimerScheduler> mpLateDestroy; // for clean exit
      97             :     static sal_Int32 mnTaskId;
      98             : 
      99             :     ::osl::Mutex maTaskContainerMutex;
     100             :     typedef ::std::set<SharedTimerTask,TimerTaskComparator> TaskContainer;
     101             :     TaskContainer maScheduledTasks;
     102             :     ::osl::Mutex maCurrentTaskMutex;
     103             :     SharedTimerTask mpCurrentTask;
     104             : 
     105             :     TimerScheduler (void);
     106             :     virtual ~TimerScheduler (void);
     107           0 :     class Deleter {public: void operator () (TimerScheduler* pScheduler) { delete pScheduler; } };
     108             :     friend class Deleter;
     109             : 
     110             :     virtual void SAL_CALL run (void) SAL_OVERRIDE;
     111           0 :     virtual void SAL_CALL onTerminated (void) SAL_OVERRIDE { mpLateDestroy.reset(); }
     112             : };
     113             : 
     114             : } // end of anonymous namespace
     115             : 
     116             : //===== PresenterTimer ========================================================
     117             : 
     118           0 : sal_Int32 PresenterTimer::ScheduleRepeatedTask (
     119             :     const Task& rTask,
     120             :     const sal_Int64 nDelay,
     121             :     const sal_Int64 nIntervall)
     122             : {
     123             :     TimeValue aCurrentTime;
     124           0 :     if (TimerScheduler::GetCurrentTime(aCurrentTime))
     125             :     {
     126             :         TimeValue aDueTime;
     127             :         TimerScheduler::ConvertToTimeValue(
     128             :             aDueTime,
     129           0 :             TimerScheduler::ConvertFromTimeValue (aCurrentTime) + nDelay);
     130           0 :         SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, aDueTime, nIntervall));
     131           0 :         TimerScheduler::Instance()->ScheduleTask(pTask);
     132           0 :         return pTask->mnTaskId;
     133             :     }
     134             : 
     135           0 :     return NotAValidTaskId;
     136             : }
     137             : 
     138           0 : void PresenterTimer::CancelTask (const sal_Int32 nTaskId)
     139             : {
     140           0 :     return TimerScheduler::Instance()->CancelTask(nTaskId);
     141             : }
     142             : 
     143             : //===== TimerScheduler ========================================================
     144             : 
     145           4 : ::boost::shared_ptr<TimerScheduler> TimerScheduler::mpInstance;
     146           4 : ::osl::Mutex TimerScheduler::maInstanceMutex;
     147             : sal_Int32 TimerScheduler::mnTaskId = PresenterTimer::NotAValidTaskId;
     148             : 
     149           0 : ::boost::shared_ptr<TimerScheduler> TimerScheduler::Instance (void)
     150             : {
     151           0 :     ::osl::MutexGuard aGuard (maInstanceMutex);
     152           0 :     if (mpInstance.get() == NULL)
     153             :     {
     154           0 :         mpInstance.reset(new TimerScheduler(), TimerScheduler::Deleter());
     155           0 :         mpInstance->create();
     156             :     }
     157           0 :     return mpInstance;
     158             : }
     159             : 
     160           0 : TimerScheduler::TimerScheduler (void)
     161             :     : maTaskContainerMutex(),
     162             :       maScheduledTasks(),
     163             :       maCurrentTaskMutex(),
     164           0 :       mpCurrentTask()
     165             : {
     166           0 : }
     167             : 
     168           0 : TimerScheduler::~TimerScheduler (void)
     169             : {
     170           0 : }
     171             : 
     172           0 : SharedTimerTask TimerScheduler::CreateTimerTask (
     173             :     const PresenterTimer::Task& rTask,
     174             :     const TimeValue& rDueTime,
     175             :     const sal_Int64 nRepeatIntervall)
     176             : {
     177           0 :     return SharedTimerTask(new TimerTask(rTask, rDueTime, nRepeatIntervall, ++mnTaskId));
     178             : }
     179             : 
     180           0 : void TimerScheduler::ScheduleTask (const SharedTimerTask& rpTask)
     181             : {
     182           0 :     if (rpTask.get() == NULL)
     183           0 :         return;
     184           0 :     if (rpTask->mbIsCanceled)
     185           0 :         return;
     186             : 
     187             :     {
     188           0 :         osl::MutexGuard aTaskGuard (maTaskContainerMutex);
     189           0 :         maScheduledTasks.insert(rpTask);
     190             :     }
     191             : }
     192             : 
     193           0 : void TimerScheduler::CancelTask (const sal_Int32 nTaskId)
     194             : {
     195             :     // Set of scheduled tasks is sorted after their due times, not their
     196             :     // task ids.  Therefore we have to do a linear search for the task to
     197             :     // cancel.
     198             :     {
     199           0 :         ::osl::MutexGuard aGuard (maTaskContainerMutex);
     200           0 :         TaskContainer::iterator iTask (maScheduledTasks.begin());
     201           0 :         TaskContainer::const_iterator iEnd (maScheduledTasks.end());
     202           0 :         for ( ; iTask!=iEnd; ++iTask)
     203             :         {
     204           0 :             if ((*iTask)->mnTaskId == nTaskId)
     205             :             {
     206           0 :                 maScheduledTasks.erase(iTask);
     207           0 :                 break;
     208             :             }
     209           0 :         }
     210             :     }
     211             : 
     212             :     // The task that is to be canceled may be currently about to be
     213             :     // processed.  Mark it with a flag that a) prevents a repeating task
     214             :     // from being scheduled again and b) tries to prevent its execution.
     215             :     {
     216           0 :         ::osl::MutexGuard aGuard (maCurrentTaskMutex);
     217           0 :         if (mpCurrentTask.get() != NULL
     218           0 :             && mpCurrentTask->mnTaskId == nTaskId)
     219           0 :             mpCurrentTask->mbIsCanceled = true;
     220             :     }
     221             : 
     222             :     // Let the main-loop cleanup in it's own time
     223           0 : }
     224             : 
     225           0 : void SAL_CALL TimerScheduler::run (void)
     226             : {
     227             :     while (true)
     228             :     {
     229             :         // Get the current time.
     230             :         TimeValue aCurrentTime;
     231           0 :         if ( ! GetCurrentTime(aCurrentTime))
     232             :         {
     233             :             // We can not get the current time and thus can not schedule anything.
     234           0 :             break;
     235             :         }
     236             : 
     237             :         // Restrict access to the maScheduledTasks member to one, mutext
     238             :         // guarded, block.
     239           0 :         SharedTimerTask pTask;
     240           0 :         sal_Int64 nDifference = 0;
     241             :         {
     242           0 :             ::osl::MutexGuard aGuard (maTaskContainerMutex);
     243             : 
     244             :             // There are no more scheduled task.  Leave this loop, function and
     245             :             // live of the TimerScheduler.
     246           0 :             if (maScheduledTasks.empty())
     247           0 :                 break;
     248             : 
     249             :             nDifference = GetTimeDifference(
     250           0 :                 (*maScheduledTasks.begin())->maDueTime,
     251           0 :                 aCurrentTime);
     252           0 :             if (nDifference <= 0)
     253             :             {
     254           0 :                 pTask = *maScheduledTasks.begin();
     255           0 :                 maScheduledTasks.erase(maScheduledTasks.begin());
     256           0 :             }
     257             :         }
     258             : 
     259             :         // Acquire a reference to the current task.
     260             :         {
     261           0 :             ::osl::MutexGuard aGuard (maCurrentTaskMutex);
     262           0 :             mpCurrentTask = pTask;
     263             :         }
     264             : 
     265           0 :         if (pTask.get() == NULL)
     266             :         {
     267             :             // Wait until the first task becomes due.
     268             :             TimeValue aTimeValue;
     269           0 :             ConvertToTimeValue(aTimeValue, nDifference);
     270           0 :             wait(aTimeValue);
     271             :         }
     272             :         else
     273             :         {
     274             :             // Execute task.
     275           0 :             if ( ! pTask->maTask.empty()
     276           0 :                 && ! pTask->mbIsCanceled)
     277             :             {
     278           0 :                 pTask->maTask(aCurrentTime);
     279             : 
     280             :                 // Re-schedule repeating tasks.
     281           0 :                 if (pTask->mnRepeatIntervall > 0)
     282             :                 {
     283             :                     ConvertToTimeValue(
     284           0 :                         pTask->maDueTime,
     285           0 :                         ConvertFromTimeValue(pTask->maDueTime)
     286           0 :                             + pTask->mnRepeatIntervall);
     287           0 :                     ScheduleTask(pTask);
     288             :                 }
     289             :             }
     290             : 
     291             :         }
     292             : 
     293             :         // Release reference to the current task.
     294             :         {
     295           0 :             ::osl::MutexGuard aGuard (maCurrentTaskMutex);
     296           0 :             mpCurrentTask.reset();
     297             :         }
     298           0 :     }
     299             : 
     300             :     // While holding maInstanceMutex
     301           0 :     osl::Guard< osl::Mutex > aInstance( maInstanceMutex );
     302           0 :     mpLateDestroy = mpInstance;
     303           0 :     mpInstance.reset();
     304           0 : }
     305             : 
     306           0 : bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime)
     307             : {
     308             :     TimeValue aSystemTime;
     309           0 :     if (osl_getSystemTime(&aSystemTime))
     310           0 :         return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime);
     311           0 :     return false;
     312             : }
     313             : 
     314           0 : sal_Int64 TimerScheduler::GetTimeDifference (
     315             :     const TimeValue& rTargetTime,
     316             :     const TimeValue& rCurrentTime)
     317             : {
     318           0 :     return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime);
     319             : }
     320             : 
     321           0 : void TimerScheduler::ConvertToTimeValue (
     322             :     TimeValue& rTimeValue,
     323             :     const sal_Int64 nTimeDifference)
     324             : {
     325           0 :     rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L);
     326           0 :     rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L);
     327           0 : }
     328             : 
     329           0 : sal_Int64 TimerScheduler::ConvertFromTimeValue (
     330             :     const TimeValue& rTimeValue)
     331             : {
     332           0 :     return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec;
     333             : }
     334             : 
     335             : //===== TimerTask =============================================================
     336             : 
     337             : namespace {
     338             : 
     339           0 : TimerTask::TimerTask (
     340             :     const PresenterTimer::Task& rTask,
     341             :     const TimeValue& rDueTime,
     342             :     const sal_Int64 nRepeatIntervall,
     343             :     const sal_Int32 nTaskId)
     344             :     : maTask(rTask),
     345             :       maDueTime(rDueTime),
     346             :       mnRepeatIntervall(nRepeatIntervall),
     347             :       mnTaskId(nTaskId),
     348           0 :       mbIsCanceled(false)
     349             : {
     350           0 : }
     351             : 
     352             : } // end of anonymous namespace
     353             : 
     354             : //===== PresenterTimer ========================================================
     355             : 
     356           4 : ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::mpInstance;
     357             : 
     358           0 : ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::Instance (
     359             :     const css::uno::Reference<css::uno::XComponentContext>& rxContext)
     360             : {
     361           0 :     ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex());
     362             : 
     363           0 :     ::rtl::Reference<PresenterClockTimer> pTimer;
     364           0 :     if (mpInstance.is())
     365             :     {
     366           0 :         pTimer = mpInstance;
     367             :     }
     368           0 :     if ( ! pTimer.is())
     369             :     {
     370           0 :         pTimer = ::rtl::Reference<PresenterClockTimer>(new PresenterClockTimer(rxContext));
     371           0 :         mpInstance = pTimer;
     372             :     }
     373           0 :     return pTimer;
     374             : }
     375             : 
     376           0 : PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext)
     377             :     : PresenterClockTimerInterfaceBase(m_aMutex),
     378             :       maListeners(),
     379             :       maDateTime(),
     380             :       mnTimerTaskId(PresenterTimer::NotAValidTaskId),
     381             :       mbIsCallbackPending(false),
     382           0 :       mxRequestCallback()
     383             : {
     384             :     Reference<lang::XMultiComponentFactory> xFactory (
     385           0 :         rxContext->getServiceManager(), UNO_QUERY);
     386           0 :     if (xFactory.is())
     387           0 :         mxRequestCallback = Reference<awt::XRequestCallback>(
     388           0 :             xFactory->createInstanceWithContext(
     389             :                 "com.sun.star.awt.AsyncCallback",
     390           0 :                 rxContext),
     391           0 :             UNO_QUERY_THROW);
     392           0 : }
     393             : 
     394           0 : PresenterClockTimer::~PresenterClockTimer (void)
     395             : {
     396           0 :     if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
     397             :     {
     398           0 :         PresenterTimer::CancelTask(mnTimerTaskId);
     399           0 :         mnTimerTaskId = PresenterTimer::NotAValidTaskId;
     400             :     }
     401             : 
     402           0 :     Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY);
     403           0 :     if (xComponent.is())
     404           0 :         xComponent->dispose();
     405           0 :     mxRequestCallback = NULL;
     406           0 : }
     407             : 
     408           0 : void PresenterClockTimer::AddListener (const SharedListener& rListener)
     409             : {
     410           0 :     osl::MutexGuard aGuard (maMutex);
     411             : 
     412           0 :     maListeners.push_back(rListener);
     413             : 
     414             :     // Create a timer task when the first listener is added.
     415           0 :     if (mnTimerTaskId==PresenterTimer::NotAValidTaskId)
     416             :     {
     417             :         mnTimerTaskId = PresenterTimer::ScheduleRepeatedTask(
     418             :             ::boost::bind(&PresenterClockTimer::CheckCurrentTime, this, _1),
     419             :             0,
     420           0 :             250000000 /*ns*/);
     421           0 :     }
     422           0 : }
     423             : 
     424           0 : void PresenterClockTimer::RemoveListener (const SharedListener& rListener)
     425             : {
     426           0 :     osl::MutexGuard aGuard (maMutex);
     427             : 
     428             :     ListenerContainer::iterator iListener (::std::find(
     429             :         maListeners.begin(),
     430             :         maListeners.end(),
     431           0 :         rListener));
     432           0 :     if (iListener != maListeners.end())
     433           0 :         maListeners.erase(iListener);
     434           0 :     if (maListeners.empty())
     435             :     {
     436             :         // We have no more clients and therefore are not interested in time changes.
     437           0 :         if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
     438             :         {
     439           0 :             PresenterTimer::CancelTask(mnTimerTaskId);
     440           0 :             mnTimerTaskId = PresenterTimer::NotAValidTaskId;
     441             :         }
     442           0 :         mpInstance = NULL;
     443           0 :     }
     444           0 : }
     445             : 
     446           0 : oslDateTime PresenterClockTimer::GetCurrentTime (void)
     447             : {
     448             :     TimeValue aCurrentTime;
     449           0 :     TimerScheduler::GetCurrentTime(aCurrentTime);
     450             :     oslDateTime aDateTime;
     451           0 :     osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime);
     452           0 :     return aDateTime;
     453             : }
     454             : 
     455           0 : void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime)
     456             : {
     457           0 :     css::uno::Reference<css::awt::XRequestCallback> xRequestCallback;
     458           0 :     css::uno::Reference<css::awt::XCallback> xCallback;
     459             :     {
     460           0 :         osl::MutexGuard aGuard (maMutex);
     461             : 
     462           0 :         TimeValue aCurrentTime (rCurrentTime);
     463             :         oslDateTime aDateTime;
     464           0 :         if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime))
     465             :         {
     466           0 :             if (aDateTime.Seconds != maDateTime.Seconds
     467           0 :                 || aDateTime.Minutes != maDateTime.Minutes
     468           0 :                 || aDateTime.Hours != maDateTime.Hours)
     469             :             {
     470             :                 // The displayed part of the current time has changed.
     471             :                 // Prepare to call the listeners.
     472           0 :                 maDateTime = aDateTime;
     473             : 
     474             :                 // Schedule notification of listeners.
     475           0 :                 if (mxRequestCallback.is() && ! mbIsCallbackPending)
     476             :                 {
     477           0 :                     mbIsCallbackPending = true;
     478           0 :                     xRequestCallback = mxRequestCallback;
     479           0 :                     xCallback = this;
     480             :                 }
     481             :             }
     482           0 :         }
     483             :     }
     484           0 :     if (mxRequestCallback.is() && xCallback.is())
     485           0 :         xRequestCallback->addCallback(xCallback, Any());
     486           0 : }
     487             : 
     488             : //----- XCallback -------------------------------------------------------------
     489             : 
     490           0 : void SAL_CALL PresenterClockTimer::notify (const css::uno::Any& rUserData)
     491             :     throw (css::uno::RuntimeException, std::exception)
     492             : {
     493             :     (void)rUserData;
     494             : 
     495           0 :     ListenerContainer aListenerCopy (maListeners);
     496             : 
     497             :     {
     498           0 :         osl::MutexGuard aGuard (maMutex);
     499             : 
     500           0 :         mbIsCallbackPending = false;
     501             : 
     502             :         ::std::copy(
     503             :             maListeners.begin(),
     504             :             maListeners.end(),
     505           0 :             ::std::back_inserter(aListenerCopy));
     506             :     }
     507             : 
     508           0 :     if (aListenerCopy.size() > 0)
     509             :     {
     510           0 :         ListenerContainer::const_iterator iListener;
     511           0 :         ListenerContainer::const_iterator iEnd (aListenerCopy.end());
     512           0 :         for (iListener=aListenerCopy.begin(); iListener!=iEnd; ++iListener)
     513             :         {
     514           0 :             (*iListener)->TimeHasChanged(maDateTime);
     515             :         }
     516           0 :     }
     517           0 : }
     518             : 
     519          12 : } } // end of namespace ::sdext::presenter
     520             : 
     521             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.10