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