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