1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
31 using namespace ::com::sun::star
;
32 using namespace ::com::sun::star::uno
;
34 namespace sdext
{ namespace presenter
{
41 const PresenterTimer::Task
& rTask
,
42 const TimeValue
& rDueTime
,
43 const sal_Int64 nRepeatIntervall
,
44 const sal_Int32 nTaskId
);
47 PresenterTimer::Task maTask
;
49 const sal_Int64 mnRepeatIntervall
;
50 const sal_Int32 mnTaskId
;
54 typedef ::boost::shared_ptr
<TimerTask
> SharedTimerTask
;
56 class TimerTaskComparator
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.
70 : public ::boost::enable_shared_from_this
<TimerScheduler
>,
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
);
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
;
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 (
120 const sal_Int64 nDelay
,
121 const sal_Int64 nIntervall
)
123 TimeValue aCurrentTime
;
124 if (TimerScheduler::GetCurrentTime(aCurrentTime
))
127 TimerScheduler::ConvertToTimeValue(
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();
160 TimerScheduler::TimerScheduler()
161 : maTaskContainerMutex(),
163 maCurrentTaskMutex(),
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
)
184 if (rpTask
->mbIsCanceled
)
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
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
);
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");
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.
239 // Restrict access to the maScheduledTasks member to one, mutext
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())
251 nDifference
= GetTimeDifference(
252 (*maScheduledTasks
.begin())->maDueTime
,
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
);
277 if ( ! pTask
->maTask
.empty()
278 && ! pTask
->mbIsCanceled
)
280 pTask
->maTask(aCurrentTime
);
282 // Re-schedule repeating tasks.
283 if (pTask
->mnRepeatIntervall
> 0)
287 ConvertFromTimeValue(pTask
->maDueTime
)
288 + pTask
->mnRepeatIntervall
);
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
;
308 bool TimerScheduler::GetCurrentTime (TimeValue
& rCurrentTime
)
310 TimeValue aSystemTime
;
311 if (osl_getSystemTime(&aSystemTime
))
312 return osl_getLocalTimeFromSystemTime(&aSystemTime
, &rCurrentTime
);
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 =============================================================
341 TimerTask::TimerTask (
342 const PresenterTimer::Task
& rTask
,
343 const TimeValue
& rDueTime
,
344 const sal_Int64 nRepeatIntervall
,
345 const sal_Int32 nTaskId
)
348 mnRepeatIntervall(nRepeatIntervall
),
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
;
372 pTimer
= ::rtl::Reference
<PresenterClockTimer
>(new PresenterClockTimer(rxContext
));
378 PresenterClockTimer::PresenterClockTimer (const Reference
<XComponentContext
>& rxContext
)
379 : PresenterClockTimerInterfaceBase(m_aMutex
),
382 mnTimerTaskId(PresenterTimer::NotAValidTaskId
),
383 mbIsCallbackPending(false),
386 Reference
<lang::XMultiComponentFactory
> xFactory (
387 rxContext
->getServiceManager(), UNO_QUERY
);
389 mxRequestCallback
= Reference
<awt::XRequestCallback
>(
390 xFactory
->createInstanceWithContext(
391 "com.sun.star.awt.AsyncCallback",
396 PresenterClockTimer::~PresenterClockTimer()
398 if (mnTimerTaskId
!= PresenterTimer::NotAValidTaskId
)
400 PresenterTimer::CancelTask(mnTimerTaskId
);
401 mnTimerTaskId
= PresenterTimer::NotAValidTaskId
;
404 Reference
<lang::XComponent
> xComponent (mxRequestCallback
, UNO_QUERY
);
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
),
426 void PresenterClockTimer::RemoveListener (const SharedListener
& rListener
)
428 osl::MutexGuard
aGuard (maMutex
);
430 ListenerContainer::iterator
iListener (::std::find(
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
;
448 oslDateTime
PresenterClockTimer::GetCurrentTime()
450 TimeValue aCurrentTime
;
451 TimerScheduler::GetCurrentTime(aCurrentTime
);
452 oslDateTime aDateTime
;
453 osl_getDateTimeFromTimeValue(&aCurrentTime
, &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
;
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
)
497 ListenerContainer
aListenerCopy (maListeners
);
500 osl::MutexGuard
aGuard (maMutex
);
502 mbIsCallbackPending
= false;
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: */