LCOV - code coverage report
Current view: top level - sdext/source/presenter - PresenterTimer.cxx (source / functions) Hit Total Coverage
Test: commit c8344322a7af75b84dd3ca8f78b05543a976dfd5 Lines: 4 184 2.2 %
Date: 2015-06-13 12:38:46 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() {}
      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();
      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();
     106             :     virtual ~TimerScheduler();
     107           0 :     class Deleter {public: void operator () (TimerScheduler* pScheduler) { delete pScheduler; } };
     108             :     friend class Deleter;
     109             : 
     110             :     virtual void SAL_CALL run() SAL_OVERRIDE;
     111           0 :     virtual void SAL_CALL onTerminated() 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           6 : ::boost::shared_ptr<TimerScheduler> TimerScheduler::mpInstance;
     146           6 : ::osl::Mutex TimerScheduler::maInstanceMutex;
     147             : sal_Int32 TimerScheduler::mnTaskId = PresenterTimer::NotAValidTaskId;
     148             : 
     149           0 : ::boost::shared_ptr<TimerScheduler> TimerScheduler::Instance()
     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()
     161             :     : maTaskContainerMutex(),
     162             :       maScheduledTasks(),
     163             :       maCurrentTaskMutex(),
     164           0 :       mpCurrentTask()
     165             : {
     166           0 : }
     167             : 
     168           0 : TimerScheduler::~TimerScheduler()
     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()
     226             : {
     227           0 :     osl_setThreadName("sdext::presenter::TimerScheduler");
     228             : 
     229             :     while (true)
     230             :     {
     231             :         // Get the current time.
     232             :         TimeValue aCurrentTime;
     233           0 :         if ( ! GetCurrentTime(aCurrentTime))
     234             :         {
     235             :             // We can not get the current time and thus can not schedule anything.
     236           0 :             break;
     237             :         }
     238             : 
     239             :         // Restrict access to the maScheduledTasks member to one, mutext
     240             :         // guarded, block.
     241           0 :         SharedTimerTask pTask;
     242           0 :         sal_Int64 nDifference = 0;
     243             :         {
     244           0 :             ::osl::MutexGuard aGuard (maTaskContainerMutex);
     245             : 
     246             :             // There are no more scheduled task.  Leave this loop, function and
     247             :             // live of the TimerScheduler.
     248           0 :             if (maScheduledTasks.empty())
     249           0 :                 break;
     250             : 
     251             :             nDifference = GetTimeDifference(
     252           0 :                 (*maScheduledTasks.begin())->maDueTime,
     253           0 :                 aCurrentTime);
     254           0 :             if (nDifference <= 0)
     255             :             {
     256           0 :                 pTask = *maScheduledTasks.begin();
     257           0 :                 maScheduledTasks.erase(maScheduledTasks.begin());
     258           0 :             }
     259             :         }
     260             : 
     261             :         // Acquire a reference to the current task.
     262             :         {
     263           0 :             ::osl::MutexGuard aGuard (maCurrentTaskMutex);
     264           0 :             mpCurrentTask = pTask;
     265             :         }
     266             : 
     267           0 :         if (pTask.get() == NULL)
     268             :         {
     269             :             // Wait until the first task becomes due.
     270             :             TimeValue aTimeValue;
     271           0 :             ConvertToTimeValue(aTimeValue, nDifference);
     272           0 :             wait(aTimeValue);
     273             :         }
     274             :         else
     275             :         {
     276             :             // Execute task.
     277           0 :             if ( ! pTask->maTask.empty()
     278           0 :                 && ! pTask->mbIsCanceled)
     279             :             {
     280           0 :                 pTask->maTask(aCurrentTime);
     281             : 
     282             :                 // Re-schedule repeating tasks.
     283           0 :                 if (pTask->mnRepeatIntervall > 0)
     284             :                 {
     285             :                     ConvertToTimeValue(
     286           0 :                         pTask->maDueTime,
     287           0 :                         ConvertFromTimeValue(pTask->maDueTime)
     288           0 :                             + pTask->mnRepeatIntervall);
     289           0 :                     ScheduleTask(pTask);
     290             :                 }
     291             :             }
     292             : 
     293             :         }
     294             : 
     295             :         // Release reference to the current task.
     296             :         {
     297           0 :             ::osl::MutexGuard aGuard (maCurrentTaskMutex);
     298           0 :             mpCurrentTask.reset();
     299             :         }
     300           0 :     }
     301             : 
     302             :     // While holding maInstanceMutex
     303           0 :     osl::Guard< osl::Mutex > aInstance( maInstanceMutex );
     304           0 :     mpLateDestroy = mpInstance;
     305           0 :     mpInstance.reset();
     306           0 : }
     307             : 
     308           0 : bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime)
     309             : {
     310             :     TimeValue aSystemTime;
     311           0 :     if (osl_getSystemTime(&aSystemTime))
     312           0 :         return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime);
     313           0 :     return false;
     314             : }
     315             : 
     316           0 : sal_Int64 TimerScheduler::GetTimeDifference (
     317             :     const TimeValue& rTargetTime,
     318             :     const TimeValue& rCurrentTime)
     319             : {
     320           0 :     return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime);
     321             : }
     322             : 
     323           0 : void TimerScheduler::ConvertToTimeValue (
     324             :     TimeValue& rTimeValue,
     325             :     const sal_Int64 nTimeDifference)
     326             : {
     327           0 :     rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L);
     328           0 :     rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L);
     329           0 : }
     330             : 
     331           0 : sal_Int64 TimerScheduler::ConvertFromTimeValue (
     332             :     const TimeValue& rTimeValue)
     333             : {
     334           0 :     return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec;
     335             : }
     336             : 
     337             : //===== TimerTask =============================================================
     338             : 
     339             : namespace {
     340             : 
     341           0 : TimerTask::TimerTask (
     342             :     const PresenterTimer::Task& rTask,
     343             :     const TimeValue& rDueTime,
     344             :     const sal_Int64 nRepeatIntervall,
     345             :     const sal_Int32 nTaskId)
     346             :     : maTask(rTask),
     347             :       maDueTime(rDueTime),
     348             :       mnRepeatIntervall(nRepeatIntervall),
     349             :       mnTaskId(nTaskId),
     350           0 :       mbIsCanceled(false)
     351             : {
     352           0 : }
     353             : 
     354             : } // end of anonymous namespace
     355             : 
     356             : //===== PresenterTimer ========================================================
     357             : 
     358           6 : ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::mpInstance;
     359             : 
     360           0 : ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::Instance (
     361             :     const css::uno::Reference<css::uno::XComponentContext>& rxContext)
     362             : {
     363           0 :     ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex());
     364             : 
     365           0 :     ::rtl::Reference<PresenterClockTimer> pTimer;
     366           0 :     if (mpInstance.is())
     367             :     {
     368           0 :         pTimer = mpInstance;
     369             :     }
     370           0 :     if ( ! pTimer.is())
     371             :     {
     372           0 :         pTimer = ::rtl::Reference<PresenterClockTimer>(new PresenterClockTimer(rxContext));
     373           0 :         mpInstance = pTimer;
     374             :     }
     375           0 :     return pTimer;
     376             : }
     377             : 
     378           0 : PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext)
     379             :     : PresenterClockTimerInterfaceBase(m_aMutex),
     380             :       maListeners(),
     381             :       maDateTime(),
     382             :       mnTimerTaskId(PresenterTimer::NotAValidTaskId),
     383             :       mbIsCallbackPending(false),
     384           0 :       mxRequestCallback()
     385             : {
     386             :     Reference<lang::XMultiComponentFactory> xFactory (
     387           0 :         rxContext->getServiceManager(), UNO_QUERY);
     388           0 :     if (xFactory.is())
     389           0 :         mxRequestCallback = Reference<awt::XRequestCallback>(
     390           0 :             xFactory->createInstanceWithContext(
     391             :                 "com.sun.star.awt.AsyncCallback",
     392           0 :                 rxContext),
     393           0 :             UNO_QUERY_THROW);
     394           0 : }
     395             : 
     396           0 : PresenterClockTimer::~PresenterClockTimer()
     397             : {
     398           0 :     if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
     399             :     {
     400           0 :         PresenterTimer::CancelTask(mnTimerTaskId);
     401           0 :         mnTimerTaskId = PresenterTimer::NotAValidTaskId;
     402             :     }
     403             : 
     404           0 :     Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY);
     405           0 :     if (xComponent.is())
     406           0 :         xComponent->dispose();
     407           0 :     mxRequestCallback = NULL;
     408           0 : }
     409             : 
     410           0 : void PresenterClockTimer::AddListener (const SharedListener& rListener)
     411             : {
     412           0 :     osl::MutexGuard aGuard (maMutex);
     413             : 
     414           0 :     maListeners.push_back(rListener);
     415             : 
     416             :     // Create a timer task when the first listener is added.
     417           0 :     if (mnTimerTaskId==PresenterTimer::NotAValidTaskId)
     418             :     {
     419             :         mnTimerTaskId = PresenterTimer::ScheduleRepeatedTask(
     420             :             ::boost::bind(&PresenterClockTimer::CheckCurrentTime, this, _1),
     421             :             0,
     422           0 :             250000000 /*ns*/);
     423           0 :     }
     424           0 : }
     425             : 
     426           0 : void PresenterClockTimer::RemoveListener (const SharedListener& rListener)
     427             : {
     428           0 :     osl::MutexGuard aGuard (maMutex);
     429             : 
     430             :     ListenerContainer::iterator iListener (::std::find(
     431             :         maListeners.begin(),
     432             :         maListeners.end(),
     433           0 :         rListener));
     434           0 :     if (iListener != maListeners.end())
     435           0 :         maListeners.erase(iListener);
     436           0 :     if (maListeners.empty())
     437             :     {
     438             :         // We have no more clients and therefore are not interested in time changes.
     439           0 :         if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
     440             :         {
     441           0 :             PresenterTimer::CancelTask(mnTimerTaskId);
     442           0 :             mnTimerTaskId = PresenterTimer::NotAValidTaskId;
     443             :         }
     444           0 :         mpInstance = NULL;
     445           0 :     }
     446           0 : }
     447             : 
     448           0 : oslDateTime PresenterClockTimer::GetCurrentTime()
     449             : {
     450             :     TimeValue aCurrentTime;
     451           0 :     TimerScheduler::GetCurrentTime(aCurrentTime);
     452             :     oslDateTime aDateTime;
     453           0 :     osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime);
     454           0 :     return aDateTime;
     455             : }
     456             : 
     457           0 : void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime)
     458             : {
     459           0 :     css::uno::Reference<css::awt::XRequestCallback> xRequestCallback;
     460           0 :     css::uno::Reference<css::awt::XCallback> xCallback;
     461             :     {
     462           0 :         osl::MutexGuard aGuard (maMutex);
     463             : 
     464           0 :         TimeValue aCurrentTime (rCurrentTime);
     465             :         oslDateTime aDateTime;
     466           0 :         if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime))
     467             :         {
     468           0 :             if (aDateTime.Seconds != maDateTime.Seconds
     469           0 :                 || aDateTime.Minutes != maDateTime.Minutes
     470           0 :                 || aDateTime.Hours != maDateTime.Hours)
     471             :             {
     472             :                 // The displayed part of the current time has changed.
     473             :                 // Prepare to call the listeners.
     474           0 :                 maDateTime = aDateTime;
     475             : 
     476             :                 // Schedule notification of listeners.
     477           0 :                 if (mxRequestCallback.is() && ! mbIsCallbackPending)
     478             :                 {
     479           0 :                     mbIsCallbackPending = true;
     480           0 :                     xRequestCallback = mxRequestCallback;
     481           0 :                     xCallback = this;
     482             :                 }
     483             :             }
     484           0 :         }
     485             :     }
     486           0 :     if (xRequestCallback.is() && xCallback.is())
     487           0 :         xRequestCallback->addCallback(xCallback, Any());
     488           0 : }
     489             : 
     490             : //----- XCallback -------------------------------------------------------------
     491             : 
     492           0 : void SAL_CALL PresenterClockTimer::notify (const css::uno::Any& rUserData)
     493             :     throw (css::uno::RuntimeException, std::exception)
     494             : {
     495             :     (void)rUserData;
     496             : 
     497           0 :     ListenerContainer aListenerCopy (maListeners);
     498             : 
     499             :     {
     500           0 :         osl::MutexGuard aGuard (maMutex);
     501             : 
     502           0 :         mbIsCallbackPending = false;
     503             : 
     504             :         ::std::copy(
     505             :             maListeners.begin(),
     506             :             maListeners.end(),
     507           0 :             ::std::back_inserter(aListenerCopy));
     508             :     }
     509             : 
     510           0 :     if (aListenerCopy.size() > 0)
     511             :     {
     512           0 :         ListenerContainer::const_iterator iListener;
     513           0 :         ListenerContainer::const_iterator iEnd (aListenerCopy.end());
     514           0 :         for (iListener=aListenerCopy.begin(); iListener!=iEnd; ++iListener)
     515             :         {
     516           0 :             (*iListener)->TimeHasChanged(maDateTime);
     517             :         }
     518           0 :     }
     519           0 : }
     520             : 
     521          18 : } } // end of namespace ::sdext::presenter
     522             : 
     523             : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Generated by: LCOV version 1.11