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