bump product version to 5.0.4.1
[LibreOffice.git] / sdext / source / presenter / PresenterTimer.cxx
blob0af44829382b6c916481eee965ba51ac73bbe50f
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "PresenterTimer.hxx"
21 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
22 #include <com/sun/star/uno/XComponentContext.hpp>
23 #include <osl/doublecheckedlocking.h>
24 #include <osl/thread.hxx>
25 #include <boost/bind.hpp>
26 #include <boost/function.hpp>
27 #include <boost/enable_shared_from_this.hpp>
28 #include <set>
29 #include <iterator>
31 using namespace ::com::sun::star;
32 using namespace ::com::sun::star::uno;
34 namespace sdext { namespace presenter {
36 namespace {
37 class TimerTask
39 public:
40 TimerTask (
41 const PresenterTimer::Task& rTask,
42 const TimeValue& rDueTime,
43 const sal_Int64 nRepeatIntervall,
44 const sal_Int32 nTaskId);
45 ~TimerTask() {}
47 PresenterTimer::Task maTask;
48 TimeValue maDueTime;
49 const sal_Int64 mnRepeatIntervall;
50 const sal_Int32 mnTaskId;
51 bool mbIsCanceled;
54 typedef ::boost::shared_ptr<TimerTask> SharedTimerTask;
56 class TimerTaskComparator
58 public:
59 bool operator() (const SharedTimerTask& rpTask1, const SharedTimerTask& rpTask2) const
61 return rpTask1->maDueTime.Seconds < rpTask2->maDueTime.Seconds
62 || (rpTask1->maDueTime.Seconds == rpTask2->maDueTime.Seconds
63 && rpTask1->maDueTime.Nanosec < rpTask2->maDueTime.Nanosec);
67 /** Queue all scheduled tasks and process them when their time has come.
69 class TimerScheduler
70 : public ::boost::enable_shared_from_this<TimerScheduler>,
71 public ::osl::Thread
73 public:
74 static ::boost::shared_ptr<TimerScheduler> Instance();
75 static SharedTimerTask CreateTimerTask (
76 const PresenterTimer::Task& rTask,
77 const TimeValue& rDueTime,
78 const sal_Int64 nRepeatIntervall);
80 void ScheduleTask (const SharedTimerTask& rpTask);
81 void CancelTask (const sal_Int32 nTaskId);
83 static bool GetCurrentTime (TimeValue& rCurrentTime);
84 static sal_Int64 GetTimeDifference (
85 const TimeValue& rTargetTime,
86 const TimeValue& rCurrentTime);
87 static void ConvertToTimeValue (
88 TimeValue& rTimeValue,
89 const sal_Int64 nTimeDifference);
90 static sal_Int64 ConvertFromTimeValue (
91 const TimeValue& rTimeValue);
93 private:
94 static ::boost::shared_ptr<TimerScheduler> mpInstance;
95 static ::osl::Mutex maInstanceMutex;
96 ::boost::shared_ptr<TimerScheduler> mpLateDestroy; // for clean exit
97 static sal_Int32 mnTaskId;
99 ::osl::Mutex maTaskContainerMutex;
100 typedef ::std::set<SharedTimerTask,TimerTaskComparator> TaskContainer;
101 TaskContainer maScheduledTasks;
102 ::osl::Mutex maCurrentTaskMutex;
103 SharedTimerTask mpCurrentTask;
105 TimerScheduler();
106 virtual ~TimerScheduler();
107 class Deleter {public: void operator () (TimerScheduler* pScheduler) { delete pScheduler; } };
108 friend class Deleter;
110 virtual void SAL_CALL run() SAL_OVERRIDE;
111 virtual void SAL_CALL onTerminated() SAL_OVERRIDE { mpLateDestroy.reset(); }
114 } // end of anonymous namespace
116 //===== PresenterTimer ========================================================
118 sal_Int32 PresenterTimer::ScheduleRepeatedTask (
119 const Task& rTask,
120 const sal_Int64 nDelay,
121 const sal_Int64 nIntervall)
123 TimeValue aCurrentTime;
124 if (TimerScheduler::GetCurrentTime(aCurrentTime))
126 TimeValue aDueTime;
127 TimerScheduler::ConvertToTimeValue(
128 aDueTime,
129 TimerScheduler::ConvertFromTimeValue (aCurrentTime) + nDelay);
130 SharedTimerTask pTask (TimerScheduler::CreateTimerTask(rTask, aDueTime, nIntervall));
131 TimerScheduler::Instance()->ScheduleTask(pTask);
132 return pTask->mnTaskId;
135 return NotAValidTaskId;
138 void PresenterTimer::CancelTask (const sal_Int32 nTaskId)
140 return TimerScheduler::Instance()->CancelTask(nTaskId);
143 //===== TimerScheduler ========================================================
145 ::boost::shared_ptr<TimerScheduler> TimerScheduler::mpInstance;
146 ::osl::Mutex TimerScheduler::maInstanceMutex;
147 sal_Int32 TimerScheduler::mnTaskId = PresenterTimer::NotAValidTaskId;
149 ::boost::shared_ptr<TimerScheduler> TimerScheduler::Instance()
151 ::osl::MutexGuard aGuard (maInstanceMutex);
152 if (mpInstance.get() == NULL)
154 mpInstance.reset(new TimerScheduler(), TimerScheduler::Deleter());
155 mpInstance->create();
157 return mpInstance;
160 TimerScheduler::TimerScheduler()
161 : maTaskContainerMutex(),
162 maScheduledTasks(),
163 maCurrentTaskMutex(),
164 mpCurrentTask()
168 TimerScheduler::~TimerScheduler()
172 SharedTimerTask TimerScheduler::CreateTimerTask (
173 const PresenterTimer::Task& rTask,
174 const TimeValue& rDueTime,
175 const sal_Int64 nRepeatIntervall)
177 return SharedTimerTask(new TimerTask(rTask, rDueTime, nRepeatIntervall, ++mnTaskId));
180 void TimerScheduler::ScheduleTask (const SharedTimerTask& rpTask)
182 if (rpTask.get() == NULL)
183 return;
184 if (rpTask->mbIsCanceled)
185 return;
188 osl::MutexGuard aTaskGuard (maTaskContainerMutex);
189 maScheduledTasks.insert(rpTask);
193 void TimerScheduler::CancelTask (const sal_Int32 nTaskId)
195 // Set of scheduled tasks is sorted after their due times, not their
196 // task ids. Therefore we have to do a linear search for the task to
197 // cancel.
199 ::osl::MutexGuard aGuard (maTaskContainerMutex);
200 TaskContainer::iterator iTask (maScheduledTasks.begin());
201 TaskContainer::const_iterator iEnd (maScheduledTasks.end());
202 for ( ; iTask!=iEnd; ++iTask)
204 if ((*iTask)->mnTaskId == nTaskId)
206 maScheduledTasks.erase(iTask);
207 break;
212 // The task that is to be canceled may be currently about to be
213 // processed. Mark it with a flag that a) prevents a repeating task
214 // from being scheduled again and b) tries to prevent its execution.
216 ::osl::MutexGuard aGuard (maCurrentTaskMutex);
217 if (mpCurrentTask.get() != NULL
218 && mpCurrentTask->mnTaskId == nTaskId)
219 mpCurrentTask->mbIsCanceled = true;
222 // Let the main-loop cleanup in it's own time
225 void SAL_CALL TimerScheduler::run()
227 osl_setThreadName("sdext::presenter::TimerScheduler");
229 while (true)
231 // Get the current time.
232 TimeValue aCurrentTime;
233 if ( ! GetCurrentTime(aCurrentTime))
235 // We can not get the current time and thus can not schedule anything.
236 break;
239 // Restrict access to the maScheduledTasks member to one, mutext
240 // guarded, block.
241 SharedTimerTask pTask;
242 sal_Int64 nDifference = 0;
244 ::osl::MutexGuard aGuard (maTaskContainerMutex);
246 // There are no more scheduled task. Leave this loop, function and
247 // live of the TimerScheduler.
248 if (maScheduledTasks.empty())
249 break;
251 nDifference = GetTimeDifference(
252 (*maScheduledTasks.begin())->maDueTime,
253 aCurrentTime);
254 if (nDifference <= 0)
256 pTask = *maScheduledTasks.begin();
257 maScheduledTasks.erase(maScheduledTasks.begin());
261 // Acquire a reference to the current task.
263 ::osl::MutexGuard aGuard (maCurrentTaskMutex);
264 mpCurrentTask = pTask;
267 if (pTask.get() == NULL)
269 // Wait until the first task becomes due.
270 TimeValue aTimeValue;
271 ConvertToTimeValue(aTimeValue, nDifference);
272 wait(aTimeValue);
274 else
276 // Execute task.
277 if ( ! pTask->maTask.empty()
278 && ! pTask->mbIsCanceled)
280 pTask->maTask(aCurrentTime);
282 // Re-schedule repeating tasks.
283 if (pTask->mnRepeatIntervall > 0)
285 ConvertToTimeValue(
286 pTask->maDueTime,
287 ConvertFromTimeValue(pTask->maDueTime)
288 + pTask->mnRepeatIntervall);
289 ScheduleTask(pTask);
295 // Release reference to the current task.
297 ::osl::MutexGuard aGuard (maCurrentTaskMutex);
298 mpCurrentTask.reset();
302 // While holding maInstanceMutex
303 osl::Guard< osl::Mutex > aInstance( maInstanceMutex );
304 mpLateDestroy = mpInstance;
305 mpInstance.reset();
308 bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime)
310 TimeValue aSystemTime;
311 if (osl_getSystemTime(&aSystemTime))
312 return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime);
313 return false;
316 sal_Int64 TimerScheduler::GetTimeDifference (
317 const TimeValue& rTargetTime,
318 const TimeValue& rCurrentTime)
320 return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime);
323 void TimerScheduler::ConvertToTimeValue (
324 TimeValue& rTimeValue,
325 const sal_Int64 nTimeDifference)
327 rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L);
328 rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L);
331 sal_Int64 TimerScheduler::ConvertFromTimeValue (
332 const TimeValue& rTimeValue)
334 return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec;
337 //===== TimerTask =============================================================
339 namespace {
341 TimerTask::TimerTask (
342 const PresenterTimer::Task& rTask,
343 const TimeValue& rDueTime,
344 const sal_Int64 nRepeatIntervall,
345 const sal_Int32 nTaskId)
346 : maTask(rTask),
347 maDueTime(rDueTime),
348 mnRepeatIntervall(nRepeatIntervall),
349 mnTaskId(nTaskId),
350 mbIsCanceled(false)
354 } // end of anonymous namespace
356 //===== PresenterTimer ========================================================
358 ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::mpInstance;
360 ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::Instance (
361 const css::uno::Reference<css::uno::XComponentContext>& rxContext)
363 ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex());
365 ::rtl::Reference<PresenterClockTimer> pTimer;
366 if (mpInstance.is())
368 pTimer = mpInstance;
370 if ( ! pTimer.is())
372 pTimer = ::rtl::Reference<PresenterClockTimer>(new PresenterClockTimer(rxContext));
373 mpInstance = pTimer;
375 return pTimer;
378 PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext)
379 : PresenterClockTimerInterfaceBase(m_aMutex),
380 maListeners(),
381 maDateTime(),
382 mnTimerTaskId(PresenterTimer::NotAValidTaskId),
383 mbIsCallbackPending(false),
384 mxRequestCallback()
386 Reference<lang::XMultiComponentFactory> xFactory (
387 rxContext->getServiceManager(), UNO_QUERY);
388 if (xFactory.is())
389 mxRequestCallback = Reference<awt::XRequestCallback>(
390 xFactory->createInstanceWithContext(
391 "com.sun.star.awt.AsyncCallback",
392 rxContext),
393 UNO_QUERY_THROW);
396 PresenterClockTimer::~PresenterClockTimer()
398 if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
400 PresenterTimer::CancelTask(mnTimerTaskId);
401 mnTimerTaskId = PresenterTimer::NotAValidTaskId;
404 Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY);
405 if (xComponent.is())
406 xComponent->dispose();
407 mxRequestCallback = NULL;
410 void PresenterClockTimer::AddListener (const SharedListener& rListener)
412 osl::MutexGuard aGuard (maMutex);
414 maListeners.push_back(rListener);
416 // Create a timer task when the first listener is added.
417 if (mnTimerTaskId==PresenterTimer::NotAValidTaskId)
419 mnTimerTaskId = PresenterTimer::ScheduleRepeatedTask(
420 ::boost::bind(&PresenterClockTimer::CheckCurrentTime, this, _1),
422 250000000 /*ns*/);
426 void PresenterClockTimer::RemoveListener (const SharedListener& rListener)
428 osl::MutexGuard aGuard (maMutex);
430 ListenerContainer::iterator iListener (::std::find(
431 maListeners.begin(),
432 maListeners.end(),
433 rListener));
434 if (iListener != maListeners.end())
435 maListeners.erase(iListener);
436 if (maListeners.empty())
438 // We have no more clients and therefore are not interested in time changes.
439 if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
441 PresenterTimer::CancelTask(mnTimerTaskId);
442 mnTimerTaskId = PresenterTimer::NotAValidTaskId;
444 mpInstance = NULL;
448 oslDateTime PresenterClockTimer::GetCurrentTime()
450 TimeValue aCurrentTime;
451 TimerScheduler::GetCurrentTime(aCurrentTime);
452 oslDateTime aDateTime;
453 osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime);
454 return aDateTime;
457 void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime)
459 css::uno::Reference<css::awt::XRequestCallback> xRequestCallback;
460 css::uno::Reference<css::awt::XCallback> xCallback;
462 osl::MutexGuard aGuard (maMutex);
464 TimeValue aCurrentTime (rCurrentTime);
465 oslDateTime aDateTime;
466 if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime))
468 if (aDateTime.Seconds != maDateTime.Seconds
469 || aDateTime.Minutes != maDateTime.Minutes
470 || aDateTime.Hours != maDateTime.Hours)
472 // The displayed part of the current time has changed.
473 // Prepare to call the listeners.
474 maDateTime = aDateTime;
476 // Schedule notification of listeners.
477 if (mxRequestCallback.is() && ! mbIsCallbackPending)
479 mbIsCallbackPending = true;
480 xRequestCallback = mxRequestCallback;
481 xCallback = this;
486 if (xRequestCallback.is() && xCallback.is())
487 xRequestCallback->addCallback(xCallback, Any());
490 //----- XCallback -------------------------------------------------------------
492 void SAL_CALL PresenterClockTimer::notify (const css::uno::Any& rUserData)
493 throw (css::uno::RuntimeException, std::exception)
495 (void)rUserData;
497 ListenerContainer aListenerCopy (maListeners);
500 osl::MutexGuard aGuard (maMutex);
502 mbIsCallbackPending = false;
504 ::std::copy(
505 maListeners.begin(),
506 maListeners.end(),
507 ::std::back_inserter(aListenerCopy));
510 if (aListenerCopy.size() > 0)
512 ListenerContainer::const_iterator iListener;
513 ListenerContainer::const_iterator iEnd (aListenerCopy.end());
514 for (iListener=aListenerCopy.begin(); iListener!=iEnd; ++iListener)
516 (*iListener)->TimeHasChanged(maDateTime);
521 } } // end of namespace ::sdext::presenter
523 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */