xref: /trunk/main/sdext/source/presenter/PresenterTimer.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_sdext.hxx"
24 
25 #include "PresenterTimer.hxx"
26 #include <osl/doublecheckedlocking.h>
27 #include <osl/thread.hxx>
28 #include <boost/bind.hpp>
29 #include <boost/function.hpp>
30 #include <boost/enable_shared_from_this.hpp>
31 #include <set>
32 #include <iterator>
33 
34 using namespace ::com::sun::star;
35 using namespace ::com::sun::star::uno;
36 
37 #define A2S(pString) (::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(pString)))
38 
39 namespace sdext { namespace presenter {
40 
41 namespace {
42 class TimerTask
43 {
44 public:
45     TimerTask (
46         const PresenterTimer::Task& rTask,
47         const TimeValue& rDueTime,
48         const sal_Int64 nRepeatIntervall,
49         const sal_Int32 nTaskId);
~TimerTask(void)50     ~TimerTask (void) {}
51 
52     PresenterTimer::Task maTask;
53     TimeValue maDueTime;
54     const sal_Int64 mnRepeatIntervall;
55     const sal_Int32 mnTaskId;
56     bool mbIsCanceled;
57 };
58 
59 typedef ::boost::shared_ptr<TimerTask> SharedTimerTask;
60 
61 
62 class TimerTaskComparator
63 {
64 public:
operator ()(const SharedTimerTask & rpTask1,const SharedTimerTask & rpTask2) const65     bool operator() (const SharedTimerTask& rpTask1, const SharedTimerTask& rpTask2) const
66     {
67         return rpTask1->maDueTime.Seconds < rpTask2->maDueTime.Seconds
68             || (rpTask1->maDueTime.Seconds == rpTask2->maDueTime.Seconds
69                 && rpTask1->maDueTime.Nanosec < rpTask2->maDueTime.Nanosec);
70     }
71 };
72 
73 
74 
75 /** Queue all scheduled tasks and process them when their time has come.
76 */
77 class TimerScheduler
78     : public ::boost::enable_shared_from_this<TimerScheduler>,
79       public ::osl::Thread
80 {
81 public:
82     static ::boost::shared_ptr<TimerScheduler> Instance (void);
83     static SharedTimerTask CreateTimerTask (
84         const PresenterTimer::Task& rTask,
85         const TimeValue& rDueTime,
86         const sal_Int64 nRepeatIntervall);
87 
88     void ScheduleTask (const SharedTimerTask& rpTask);
89     void CancelTask (const sal_Int32 nTaskId);
90 
91     static bool GetCurrentTime (TimeValue& rCurrentTime);
92     static sal_Int64 GetTimeDifference (
93         const TimeValue& rTargetTime,
94         const TimeValue& rCurrentTime);
95     static void ConvertToTimeValue (
96         TimeValue& rTimeValue,
97         const sal_Int64 nTimeDifference);
98     static sal_Int64 ConvertFromTimeValue (
99         const TimeValue& rTimeValue);
100 
101 private:
102     static ::boost::shared_ptr<TimerScheduler> mpInstance;
103     static ::osl::Mutex maInstanceMutex;
104     static sal_Int32 mnTaskId;
105 
106     ::osl::Mutex maTaskContainerMutex;
107     typedef ::std::set<SharedTimerTask,TimerTaskComparator> TaskContainer;
108     TaskContainer maScheduledTasks;
109     bool mbIsRunning;
110     ::osl::Mutex maCurrentTaskMutex;
111     SharedTimerTask mpCurrentTask;
112 
113     static void Release (void);
114 
115     TimerScheduler (void);
116     virtual ~TimerScheduler (void);
operator ()(TimerScheduler * pScheduler)117     class Deleter {public: void operator () (TimerScheduler* pScheduler) { delete pScheduler; } };
118     friend class Deleter;
119 
120     virtual void SAL_CALL run (void);
121     virtual void SAL_CALL onTerminated (void);
122 };
123 
124 
125 
126 bool GetDateTime (oslDateTime& rDateTime);
127 } // end of anonymous namespace
128 
129 
130 //===== PresenterTimer ========================================================
131 
ScheduleSingleTaskRelative(const Task & rTask,const sal_Int64 nDelay)132 sal_Int32 PresenterTimer::ScheduleSingleTaskRelative (
133     const Task& rTask,
134     const sal_Int64 nDelay)
135 {
136     return ScheduleRepeatedTask(rTask, nDelay, 0);
137 }
138 
139 
140 
ScheduleSingleTaskAbsolute(const Task & rTask,const TimeValue & rDueTime)141 sal_Int32 PresenterTimer::ScheduleSingleTaskAbsolute (
142     const Task& rTask,
143     const TimeValue& rDueTime)
144 {
145     SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, rDueTime, 0));
146     TimerScheduler::Instance()->ScheduleTask(pTask);
147     return pTask->mnTaskId;
148 }
149 
150 
151 
ScheduleRepeatedTask(const Task & rTask,const sal_Int64 nDelay,const sal_Int64 nIntervall)152 sal_Int32 PresenterTimer::ScheduleRepeatedTask (
153     const Task& rTask,
154     const sal_Int64 nDelay,
155     const sal_Int64 nIntervall)
156 {
157     TimeValue aCurrentTime;
158     if (TimerScheduler::GetCurrentTime(aCurrentTime))
159     {
160         TimeValue aDueTime;
161         TimerScheduler::ConvertToTimeValue(
162             aDueTime,
163             TimerScheduler::ConvertFromTimeValue (aCurrentTime) + nDelay);
164         SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, aDueTime, nIntervall));
165         TimerScheduler::Instance()->ScheduleTask(pTask);
166         return pTask->mnTaskId;
167     }
168 
169     return NotAValidTaskId;
170 }
171 
172 
173 
CancelTask(const sal_Int32 nTaskId)174 void PresenterTimer::CancelTask (const sal_Int32 nTaskId)
175 {
176     return TimerScheduler::Instance()->CancelTask(nTaskId);
177 }
178 
179 
180 
181 //===== TimerScheduler ========================================================
182 
183 ::boost::shared_ptr<TimerScheduler> TimerScheduler::mpInstance;
184 ::osl::Mutex TimerScheduler::maInstanceMutex;
185 sal_Int32 TimerScheduler::mnTaskId = PresenterTimer::NotAValidTaskId;
186 
Instance(void)187 ::boost::shared_ptr<TimerScheduler> TimerScheduler::Instance (void)
188 {
189     ::boost::shared_ptr<TimerScheduler> pInstance = mpInstance;
190     if (pInstance.get() == NULL)
191     {
192         ::osl::MutexGuard aGuard (maInstanceMutex);
193         pInstance = mpInstance;
194         if (pInstance.get() == NULL)
195         {
196             pInstance.reset(new TimerScheduler(), TimerScheduler::Deleter());
197             OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
198             mpInstance = pInstance;
199         }
200     }
201     else
202     {
203         OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
204     }
205     return pInstance;
206 }
207 
208 
209 
Release(void)210 void TimerScheduler::Release (void)
211 {
212     ::osl::MutexGuard aGuard (maInstanceMutex);
213     mpInstance.reset();
214 }
215 
216 
217 
TimerScheduler(void)218 TimerScheduler::TimerScheduler (void)
219     : maTaskContainerMutex(),
220       maScheduledTasks(),
221       mbIsRunning(false),
222       maCurrentTaskMutex(),
223       mpCurrentTask()
224 {
225 }
226 
227 
228 
~TimerScheduler(void)229 TimerScheduler::~TimerScheduler (void)
230 {
231 }
232 
233 
234 
CreateTimerTask(const PresenterTimer::Task & rTask,const TimeValue & rDueTime,const sal_Int64 nRepeatIntervall)235 SharedTimerTask TimerScheduler::CreateTimerTask (
236     const PresenterTimer::Task& rTask,
237     const TimeValue& rDueTime,
238     const sal_Int64 nRepeatIntervall)
239 {
240     return SharedTimerTask(new TimerTask(rTask, rDueTime, nRepeatIntervall, ++mnTaskId));
241 }
242 
243 
244 
ScheduleTask(const SharedTimerTask & rpTask)245 void TimerScheduler::ScheduleTask (const SharedTimerTask& rpTask)
246 {
247     if (rpTask.get() == NULL)
248         return;
249     if (rpTask->mbIsCanceled)
250         return;
251 
252     osl::MutexGuard aGuard (maTaskContainerMutex);
253     maScheduledTasks.insert(rpTask);
254 
255     if ( ! mbIsRunning)
256     {
257         mbIsRunning = true;
258         create();
259     }
260 }
261 
262 
263 
CancelTask(const sal_Int32 nTaskId)264 void TimerScheduler::CancelTask (const sal_Int32 nTaskId)
265 {
266     // Set of scheduled tasks is sorted after their due times, not their
267     // task ids.  Therefore we have to do a linear search for the task to
268     // cancel.
269     {
270         ::osl::MutexGuard aGuard (maTaskContainerMutex);
271         TaskContainer::iterator iTask (maScheduledTasks.begin());
272         TaskContainer::const_iterator iEnd (maScheduledTasks.end());
273         for ( ; iTask!=iEnd; ++iTask)
274         {
275             if ((*iTask)->mnTaskId == nTaskId)
276             {
277                 maScheduledTasks.erase(iTask);
278                 break;
279             }
280         }
281     }
282 
283     // The task that is to be canceled may be currently about to be
284     // processed.  Mark it with a flag that a) prevents a repeating task
285     // from being scheduled again and b) tries to prevent its execution.
286     if (mpCurrentTask.get() != NULL
287         && mpCurrentTask->mnTaskId == nTaskId)
288     {
289         mpCurrentTask->mbIsCanceled = true;
290     }
291 
292     // When the last active task was canceled then the timer can be
293     // stopped.
294     if (maScheduledTasks.size() == 0)
295     {
296         mbIsRunning = false;
297         resume();
298         //        join();
299     }
300 }
301 
302 
303 
run(void)304 void SAL_CALL TimerScheduler::run (void)
305 {
306     while (mbIsRunning)
307     {
308         // Get the current time.
309         TimeValue aCurrentTime;
310         if ( ! GetCurrentTime(aCurrentTime))
311         {
312             // We can not get the current time and thus can not schedule anything.
313             break;
314         }
315 
316         // Restrict access to the maScheduledTasks member to one, mutex
317         // guarded, block.
318         SharedTimerTask pTask;
319         sal_Int64 nDifference = 0;
320         {
321             ::osl::MutexGuard aGuard (maTaskContainerMutex);
322 
323             // There are no more scheduled task.  Leave this loop, function and
324             // live of the TimerScheduler.
325             if (maScheduledTasks.empty())
326                 break;
327 
328             nDifference = GetTimeDifference(
329                 (*maScheduledTasks.begin())->maDueTime,
330                 aCurrentTime);
331             if (nDifference <= 0)
332             {
333                 pTask = *maScheduledTasks.begin();
334                 maScheduledTasks.erase(maScheduledTasks.begin());
335             }
336         }
337 
338         // Acquire a reference to the current task.
339         {
340             ::osl::MutexGuard aGuard (maCurrentTaskMutex);
341             mpCurrentTask = pTask;
342         }
343 
344         if (mpCurrentTask.get() == NULL)
345         {
346             // Wait until the first task becomes due.
347             TimeValue aTimeValue;
348             ConvertToTimeValue(aTimeValue, nDifference);
349             wait(aTimeValue);
350         }
351         else
352         {
353             // Execute task.
354             if ( ! mpCurrentTask->maTask.empty()
355                 && ! mpCurrentTask->mbIsCanceled)
356             {
357                 mpCurrentTask->maTask(aCurrentTime);
358 
359                 // Re-schedule repeating tasks.
360                 if (mpCurrentTask->mnRepeatIntervall > 0)
361                 {
362                     ConvertToTimeValue(
363                         mpCurrentTask->maDueTime,
364                         ConvertFromTimeValue(mpCurrentTask->maDueTime)
365                             + mpCurrentTask->mnRepeatIntervall);
366                     ScheduleTask(mpCurrentTask);
367                 }
368             }
369 
370         }
371 
372         // Release reference to the current task.
373         {
374             ::osl::MutexGuard aGuard (maCurrentTaskMutex);
375             mpCurrentTask.reset();
376         }
377     }
378 }
379 
380 
381 
onTerminated(void)382 void SAL_CALL TimerScheduler::onTerminated (void)
383 {
384     Release();
385 }
386 
387 
388 
GetCurrentTime(TimeValue & rCurrentTime)389 bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime)
390 {
391     TimeValue aSystemTime;
392     if (osl_getSystemTime(&aSystemTime))
393         return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime);
394     return false;
395 }
396 
397 
398 
GetTimeDifference(const TimeValue & rTargetTime,const TimeValue & rCurrentTime)399 sal_Int64 TimerScheduler::GetTimeDifference (
400     const TimeValue& rTargetTime,
401     const TimeValue& rCurrentTime)
402 {
403     return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime);
404 }
405 
406 
407 
ConvertToTimeValue(TimeValue & rTimeValue,const sal_Int64 nTimeDifference)408 void TimerScheduler::ConvertToTimeValue (
409     TimeValue& rTimeValue,
410     const sal_Int64 nTimeDifference)
411 {
412     rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L);
413     rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L);
414 }
415 
416 
417 
ConvertFromTimeValue(const TimeValue & rTimeValue)418 sal_Int64 TimerScheduler::ConvertFromTimeValue (
419     const TimeValue& rTimeValue)
420 {
421     return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec;
422 }
423 
424 
425 
426 //===== TimerTask =============================================================
427 
428 namespace {
429 
TimerTask(const PresenterTimer::Task & rTask,const TimeValue & rDueTime,const sal_Int64 nRepeatIntervall,const sal_Int32 nTaskId)430 TimerTask::TimerTask (
431     const PresenterTimer::Task& rTask,
432     const TimeValue& rDueTime,
433     const sal_Int64 nRepeatIntervall,
434     const sal_Int32 nTaskId)
435     : maTask(rTask),
436       maDueTime(rDueTime),
437       mnRepeatIntervall(nRepeatIntervall),
438       mnTaskId(nTaskId),
439       mbIsCanceled(false)
440 {
441 }
442 
443 } // end of anonymous namespace
444 
445 
446 
447 //===== PresenterTimer ========================================================
448 
449 
450 ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::mpInstance;
451 
Instance(const css::uno::Reference<css::uno::XComponentContext> & rxContext)452 ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::Instance (
453     const css::uno::Reference<css::uno::XComponentContext>& rxContext)
454 {
455     ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex());
456 
457     ::rtl::Reference<PresenterClockTimer> pTimer;
458     if (mpInstance.is())
459     {
460         pTimer = mpInstance;
461     }
462     if ( ! pTimer.is())
463     {
464         pTimer = ::rtl::Reference<PresenterClockTimer>(new PresenterClockTimer(rxContext));
465         mpInstance = pTimer;
466     }
467     return pTimer;
468 }
469 
470 
471 
PresenterClockTimer(const Reference<XComponentContext> & rxContext)472 PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext)
473     : PresenterClockTimerInterfaceBase(m_aMutex),
474       maListeners(),
475       maDateTime(),
476       mnTimerTaskId(PresenterTimer::NotAValidTaskId),
477       mbIsCallbackPending(false),
478       mxRequestCallback()
479 {
480     Reference<lang::XMultiComponentFactory> xFactory (
481         rxContext->getServiceManager(), UNO_QUERY);
482     if (xFactory.is())
483         mxRequestCallback = Reference<awt::XRequestCallback>(
484             xFactory->createInstanceWithContext(
485                 A2S("com.sun.star.awt.AsyncCallback"),
486                 rxContext),
487             UNO_QUERY_THROW);
488 }
489 
490 
491 
~PresenterClockTimer(void)492 PresenterClockTimer::~PresenterClockTimer (void)
493 {
494     if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
495     {
496         PresenterTimer::CancelTask(mnTimerTaskId);
497         mnTimerTaskId = PresenterTimer::NotAValidTaskId;
498     }
499 
500     Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY);
501     if (xComponent.is())
502         xComponent->dispose();
503     mxRequestCallback = NULL;
504 }
505 
506 
507 
AddListener(const SharedListener & rListener)508 void PresenterClockTimer::AddListener (const SharedListener& rListener)
509 {
510     osl::MutexGuard aGuard (maMutex);
511 
512     maListeners.push_back(rListener);
513 
514     // Create a timer task when the first listener is added.
515     if (mnTimerTaskId==PresenterTimer::NotAValidTaskId)
516     {
517         mnTimerTaskId = PresenterTimer::ScheduleRepeatedTask(
518             ::boost::bind(&PresenterClockTimer::CheckCurrentTime, this, _1),
519             0,
520             250000000 /*ns*/);
521     }
522 }
523 
524 
525 
RemoveListener(const SharedListener & rListener)526 void PresenterClockTimer::RemoveListener (const SharedListener& rListener)
527 {
528     osl::MutexGuard aGuard (maMutex);
529 
530     ListenerContainer::iterator iListener (::std::find(
531         maListeners.begin(),
532         maListeners.end(),
533         rListener));
534     if (iListener != maListeners.end())
535         maListeners.erase(iListener);
536     if (maListeners.size() == 0)
537     {
538         // We have no more clients and therefore are not interested in time changes.
539         if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
540         {
541             PresenterTimer::CancelTask(mnTimerTaskId);
542             mnTimerTaskId = PresenterTimer::NotAValidTaskId;
543         }
544         mpInstance = NULL;
545     }
546 }
547 
548 
549 
GetCurrentTime(void)550 oslDateTime PresenterClockTimer::GetCurrentTime (void)
551 {
552     TimeValue aCurrentTime;
553     TimerScheduler::GetCurrentTime(aCurrentTime);
554     oslDateTime aDateTime;
555     osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime);
556     return aDateTime;
557 }
558 
559 
560 
GetTimeDifference(const oslDateTime & rNow,const oslDateTime & rThen)561 sal_Int64 PresenterClockTimer::GetTimeDifference (
562     const oslDateTime& rNow,
563     const oslDateTime& rThen)
564 {
565     TimeValue aNow;
566     TimeValue aThen;
567     if (osl_getTimeValueFromDateTime(const_cast<oslDateTime*>(&rNow),&aNow)
568         && osl_getTimeValueFromDateTime(const_cast<oslDateTime*>(&rThen),&aThen))
569     {
570         return TimerScheduler::GetTimeDifference(aNow, aThen);
571     }
572     else
573         return -1;
574 }
575 
576 
577 
CheckCurrentTime(const TimeValue & rCurrentTime)578 void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime)
579 {
580     css::uno::Reference<css::awt::XRequestCallback> xRequestCallback;
581     css::uno::Reference<css::awt::XCallback> xCallback;
582     {
583         osl::MutexGuard aGuard (maMutex);
584 
585         TimeValue aCurrentTime (rCurrentTime);
586         oslDateTime aDateTime;
587         if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime))
588         {
589             if (aDateTime.Seconds != maDateTime.Seconds
590                 || aDateTime.Minutes != maDateTime.Minutes
591                 || aDateTime.Seconds != maDateTime.Seconds)
592             {
593                 // The displayed part of the current time has changed.
594                 // Prepare to call the listeners.
595                 maDateTime = aDateTime;
596 
597                 // Schedule notification of listeners.
598                 if (mxRequestCallback.is() && ! mbIsCallbackPending)
599                 {
600                     mbIsCallbackPending = true;
601                     xRequestCallback = mxRequestCallback;
602                     xCallback = this;
603                 }
604             }
605         }
606     }
607     if (mxRequestCallback.is() && xCallback.is())
608         xRequestCallback->addCallback(xCallback, Any());
609 }
610 
611 
612 
613 //----- XCallback -------------------------------------------------------------
614 
notify(const css::uno::Any & rUserData)615 void SAL_CALL PresenterClockTimer::notify (const css::uno::Any& rUserData)
616 {
617     (void)rUserData;
618 
619     ListenerContainer aListenerCopy (maListeners);
620 
621     {
622         osl::MutexGuard aGuard (maMutex);
623 
624         mbIsCallbackPending = false;
625 
626         ::std::copy(
627             maListeners.begin(),
628             maListeners.end(),
629             ::std::back_inserter(aListenerCopy));
630     }
631 
632     if (aListenerCopy.size() > 0)
633     {
634         ListenerContainer::const_iterator iListener;
635         ListenerContainer::const_iterator iEnd (aListenerCopy.end());
636         for (iListener=aListenerCopy.begin(); iListener!=iEnd; ++iListener)
637         {
638             (*iListener)->TimeHasChanged(maDateTime);
639         }
640     }
641 }
642 
643 } } // end of namespace ::sdext::presenter
644 
645 /* vim: set noet sw=4 ts=4: */
646