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