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