Updated core
[LibreOffice.git] / sdext / source / presenter / PresenterTimer.cxx
blobe27d0553926606a9dc0ead3766269e2efc044ad1
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 <osl/doublecheckedlocking.h>
22 #include <osl/thread.hxx>
23 #include <boost/bind.hpp>
24 #include <boost/function.hpp>
25 #include <boost/enable_shared_from_this.hpp>
26 #include <set>
27 #include <iterator>
29 using namespace ::com::sun::star;
30 using namespace ::com::sun::star::uno;
32 namespace sdext { namespace presenter {
34 namespace {
35 class TimerTask
37 public:
38 TimerTask (
39 const PresenterTimer::Task& rTask,
40 const TimeValue& rDueTime,
41 const sal_Int64 nRepeatIntervall,
42 const sal_Int32 nTaskId);
43 ~TimerTask (void) {}
45 PresenterTimer::Task maTask;
46 TimeValue maDueTime;
47 const sal_Int64 mnRepeatIntervall;
48 const sal_Int32 mnTaskId;
49 bool mbIsCanceled;
52 typedef ::boost::shared_ptr<TimerTask> SharedTimerTask;
54 class TimerTaskComparator
56 public:
57 bool operator() (const SharedTimerTask& rpTask1, const SharedTimerTask& rpTask2) const
59 return rpTask1->maDueTime.Seconds < rpTask2->maDueTime.Seconds
60 || (rpTask1->maDueTime.Seconds == rpTask2->maDueTime.Seconds
61 && rpTask1->maDueTime.Nanosec < rpTask2->maDueTime.Nanosec);
65 /** Queue all scheduled tasks and process them when their time has come.
67 class TimerScheduler
68 : public ::boost::enable_shared_from_this<TimerScheduler>,
69 public ::osl::Thread
71 public:
72 static ::boost::shared_ptr<TimerScheduler> Instance (void);
73 static SharedTimerTask CreateTimerTask (
74 const PresenterTimer::Task& rTask,
75 const TimeValue& rDueTime,
76 const sal_Int64 nRepeatIntervall);
78 void ScheduleTask (const SharedTimerTask& rpTask);
79 void CancelTask (const sal_Int32 nTaskId);
81 static bool GetCurrentTime (TimeValue& rCurrentTime);
82 static sal_Int64 GetTimeDifference (
83 const TimeValue& rTargetTime,
84 const TimeValue& rCurrentTime);
85 static void ConvertToTimeValue (
86 TimeValue& rTimeValue,
87 const sal_Int64 nTimeDifference);
88 static sal_Int64 ConvertFromTimeValue (
89 const TimeValue& rTimeValue);
91 private:
92 static ::boost::shared_ptr<TimerScheduler> mpInstance;
93 static ::osl::Mutex maInstanceMutex;
94 ::boost::shared_ptr<TimerScheduler> mpLateDestroy; // for clean exit
95 static sal_Int32 mnTaskId;
97 ::osl::Mutex maTaskContainerMutex;
98 typedef ::std::set<SharedTimerTask,TimerTaskComparator> TaskContainer;
99 TaskContainer maScheduledTasks;
100 ::osl::Mutex maCurrentTaskMutex;
101 SharedTimerTask mpCurrentTask;
103 static void Release (void);
105 TimerScheduler (void);
106 virtual ~TimerScheduler (void);
107 class Deleter {public: void operator () (TimerScheduler* pScheduler) { delete pScheduler; } };
108 friend class Deleter;
110 virtual void SAL_CALL run (void);
111 virtual void SAL_CALL onTerminated (void) { 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 (void)
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 (void)
161 : maTaskContainerMutex(),
162 maScheduledTasks(),
163 maCurrentTaskMutex(),
164 mpCurrentTask()
168 TimerScheduler::~TimerScheduler (void)
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 (void)
227 while (1)
229 // Get the current time.
230 TimeValue aCurrentTime;
231 if ( ! GetCurrentTime(aCurrentTime))
233 // We can not get the current time and thus can not schedule anything.
234 break;
237 // Restrict access to the maScheduledTasks member to one, mutext
238 // guarded, block.
239 SharedTimerTask pTask;
240 sal_Int64 nDifference = 0;
242 ::osl::MutexGuard aGuard (maTaskContainerMutex);
244 // There are no more scheduled task. Leave this loop, function and
245 // live of the TimerScheduler.
246 if (maScheduledTasks.empty())
247 break;
249 nDifference = GetTimeDifference(
250 (*maScheduledTasks.begin())->maDueTime,
251 aCurrentTime);
252 if (nDifference <= 0)
254 pTask = *maScheduledTasks.begin();
255 maScheduledTasks.erase(maScheduledTasks.begin());
259 // Acquire a reference to the current task.
261 ::osl::MutexGuard aGuard (maCurrentTaskMutex);
262 mpCurrentTask = pTask;
265 if (pTask.get() == NULL)
267 // Wait until the first task becomes due.
268 TimeValue aTimeValue;
269 ConvertToTimeValue(aTimeValue, nDifference);
270 wait(aTimeValue);
272 else
274 // Execute task.
275 if ( ! pTask->maTask.empty()
276 && ! pTask->mbIsCanceled)
278 pTask->maTask(aCurrentTime);
280 // Re-schedule repeating tasks.
281 if (pTask->mnRepeatIntervall > 0)
283 ConvertToTimeValue(
284 pTask->maDueTime,
285 ConvertFromTimeValue(pTask->maDueTime)
286 + pTask->mnRepeatIntervall);
287 ScheduleTask(pTask);
293 // Release reference to the current task.
295 ::osl::MutexGuard aGuard (maCurrentTaskMutex);
296 mpCurrentTask.reset();
300 // While holding maInstanceMutex
301 osl::Guard< osl::Mutex > aInstance( maInstanceMutex );
302 mpLateDestroy = mpInstance;
303 mpInstance.reset();
306 bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime)
308 TimeValue aSystemTime;
309 if (osl_getSystemTime(&aSystemTime))
310 return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime);
311 return false;
314 sal_Int64 TimerScheduler::GetTimeDifference (
315 const TimeValue& rTargetTime,
316 const TimeValue& rCurrentTime)
318 return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime);
321 void TimerScheduler::ConvertToTimeValue (
322 TimeValue& rTimeValue,
323 const sal_Int64 nTimeDifference)
325 rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L);
326 rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L);
329 sal_Int64 TimerScheduler::ConvertFromTimeValue (
330 const TimeValue& rTimeValue)
332 return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec;
335 //===== TimerTask =============================================================
337 namespace {
339 TimerTask::TimerTask (
340 const PresenterTimer::Task& rTask,
341 const TimeValue& rDueTime,
342 const sal_Int64 nRepeatIntervall,
343 const sal_Int32 nTaskId)
344 : maTask(rTask),
345 maDueTime(rDueTime),
346 mnRepeatIntervall(nRepeatIntervall),
347 mnTaskId(nTaskId),
348 mbIsCanceled(false)
352 } // end of anonymous namespace
354 //===== PresenterTimer ========================================================
356 ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::mpInstance;
358 ::rtl::Reference<PresenterClockTimer> PresenterClockTimer::Instance (
359 const css::uno::Reference<css::uno::XComponentContext>& rxContext)
361 ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex());
363 ::rtl::Reference<PresenterClockTimer> pTimer;
364 if (mpInstance.is())
366 pTimer = mpInstance;
368 if ( ! pTimer.is())
370 pTimer = ::rtl::Reference<PresenterClockTimer>(new PresenterClockTimer(rxContext));
371 mpInstance = pTimer;
373 return pTimer;
376 PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext)
377 : PresenterClockTimerInterfaceBase(m_aMutex),
378 maListeners(),
379 maDateTime(),
380 mnTimerTaskId(PresenterTimer::NotAValidTaskId),
381 mbIsCallbackPending(false),
382 mxRequestCallback()
384 Reference<lang::XMultiComponentFactory> xFactory (
385 rxContext->getServiceManager(), UNO_QUERY);
386 if (xFactory.is())
387 mxRequestCallback = Reference<awt::XRequestCallback>(
388 xFactory->createInstanceWithContext(
389 "com.sun.star.awt.AsyncCallback",
390 rxContext),
391 UNO_QUERY_THROW);
394 PresenterClockTimer::~PresenterClockTimer (void)
396 if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
398 PresenterTimer::CancelTask(mnTimerTaskId);
399 mnTimerTaskId = PresenterTimer::NotAValidTaskId;
402 Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY);
403 if (xComponent.is())
404 xComponent->dispose();
405 mxRequestCallback = NULL;
408 void PresenterClockTimer::AddListener (const SharedListener& rListener)
410 osl::MutexGuard aGuard (maMutex);
412 maListeners.push_back(rListener);
414 // Create a timer task when the first listener is added.
415 if (mnTimerTaskId==PresenterTimer::NotAValidTaskId)
417 mnTimerTaskId = PresenterTimer::ScheduleRepeatedTask(
418 ::boost::bind(&PresenterClockTimer::CheckCurrentTime, this, _1),
420 250000000 /*ns*/);
424 void PresenterClockTimer::RemoveListener (const SharedListener& rListener)
426 osl::MutexGuard aGuard (maMutex);
428 ListenerContainer::iterator iListener (::std::find(
429 maListeners.begin(),
430 maListeners.end(),
431 rListener));
432 if (iListener != maListeners.end())
433 maListeners.erase(iListener);
434 if (maListeners.empty())
436 // We have no more clients and therefore are not interested in time changes.
437 if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
439 PresenterTimer::CancelTask(mnTimerTaskId);
440 mnTimerTaskId = PresenterTimer::NotAValidTaskId;
442 mpInstance = NULL;
446 oslDateTime PresenterClockTimer::GetCurrentTime (void)
448 TimeValue aCurrentTime;
449 TimerScheduler::GetCurrentTime(aCurrentTime);
450 oslDateTime aDateTime;
451 osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime);
452 return aDateTime;
455 void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime)
457 css::uno::Reference<css::awt::XRequestCallback> xRequestCallback;
458 css::uno::Reference<css::awt::XCallback> xCallback;
460 osl::MutexGuard aGuard (maMutex);
462 TimeValue aCurrentTime (rCurrentTime);
463 oslDateTime aDateTime;
464 if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime))
466 if (aDateTime.Seconds != maDateTime.Seconds
467 || aDateTime.Minutes != maDateTime.Minutes
468 || aDateTime.Hours != maDateTime.Hours)
470 // The displayed part of the current time has changed.
471 // Prepare to call the listeners.
472 maDateTime = aDateTime;
474 // Schedule notification of listeners.
475 if (mxRequestCallback.is() && ! mbIsCallbackPending)
477 mbIsCallbackPending = true;
478 xRequestCallback = mxRequestCallback;
479 xCallback = this;
484 if (mxRequestCallback.is() && xCallback.is())
485 xRequestCallback->addCallback(xCallback, Any());
488 //----- XCallback -------------------------------------------------------------
490 void SAL_CALL PresenterClockTimer::notify (const css::uno::Any& rUserData)
491 throw (css::uno::RuntimeException)
493 (void)rUserData;
495 ListenerContainer aListenerCopy (maListeners);
498 osl::MutexGuard aGuard (maMutex);
500 mbIsCallbackPending = false;
502 ::std::copy(
503 maListeners.begin(),
504 maListeners.end(),
505 ::std::back_inserter(aListenerCopy));
508 if (aListenerCopy.size() > 0)
510 ListenerContainer::const_iterator iListener;
511 ListenerContainer::const_iterator iEnd (aListenerCopy.end());
512 for (iListener=aListenerCopy.begin(); iListener!=iEnd; ++iListener)
514 (*iListener)->TimeHasChanged(maDateTime);
519 } } // end of namespace ::sdext::presenter
521 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */