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