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 <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>
29 using namespace ::com::sun::star
;
30 using namespace ::com::sun::star::uno
;
32 namespace sdext
{ namespace presenter
{
39 const PresenterTimer::Task
& rTask
,
40 const TimeValue
& rDueTime
,
41 const sal_Int64 nRepeatIntervall
,
42 const sal_Int32 nTaskId
);
45 PresenterTimer::Task maTask
;
47 const sal_Int64 mnRepeatIntervall
;
48 const sal_Int32 mnTaskId
;
52 typedef ::boost::shared_ptr
<TimerTask
> SharedTimerTask
;
54 class TimerTaskComparator
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.
68 : public ::boost::enable_shared_from_this
<TimerScheduler
>,
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
);
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 (
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 (void)
151 ::osl::MutexGuard
aGuard (maInstanceMutex
);
152 if (mpInstance
.get() == NULL
)
154 mpInstance
.reset(new TimerScheduler(), TimerScheduler::Deleter());
155 mpInstance
->create();
160 TimerScheduler::TimerScheduler (void)
161 : maTaskContainerMutex(),
163 maCurrentTaskMutex(),
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
)
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 (void)
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.
237 // Restrict access to the maScheduledTasks member to one, mutext
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())
249 nDifference
= GetTimeDifference(
250 (*maScheduledTasks
.begin())->maDueTime
,
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
);
275 if ( ! pTask
->maTask
.empty()
276 && ! pTask
->mbIsCanceled
)
278 pTask
->maTask(aCurrentTime
);
280 // Re-schedule repeating tasks.
281 if (pTask
->mnRepeatIntervall
> 0)
285 ConvertFromTimeValue(pTask
->maDueTime
)
286 + pTask
->mnRepeatIntervall
);
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
;
306 bool TimerScheduler::GetCurrentTime (TimeValue
& rCurrentTime
)
308 TimeValue aSystemTime
;
309 if (osl_getSystemTime(&aSystemTime
))
310 return osl_getLocalTimeFromSystemTime(&aSystemTime
, &rCurrentTime
);
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 =============================================================
339 TimerTask::TimerTask (
340 const PresenterTimer::Task
& rTask
,
341 const TimeValue
& rDueTime
,
342 const sal_Int64 nRepeatIntervall
,
343 const sal_Int32 nTaskId
)
346 mnRepeatIntervall(nRepeatIntervall
),
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
;
370 pTimer
= ::rtl::Reference
<PresenterClockTimer
>(new PresenterClockTimer(rxContext
));
376 PresenterClockTimer::PresenterClockTimer (const Reference
<XComponentContext
>& rxContext
)
377 : PresenterClockTimerInterfaceBase(m_aMutex
),
380 mnTimerTaskId(PresenterTimer::NotAValidTaskId
),
381 mbIsCallbackPending(false),
384 Reference
<lang::XMultiComponentFactory
> xFactory (
385 rxContext
->getServiceManager(), UNO_QUERY
);
387 mxRequestCallback
= Reference
<awt::XRequestCallback
>(
388 xFactory
->createInstanceWithContext(
389 "com.sun.star.awt.AsyncCallback",
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
);
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
),
424 void PresenterClockTimer::RemoveListener (const SharedListener
& rListener
)
426 osl::MutexGuard
aGuard (maMutex
);
428 ListenerContainer::iterator
iListener (::std::find(
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
;
446 oslDateTime
PresenterClockTimer::GetCurrentTime (void)
448 TimeValue aCurrentTime
;
449 TimerScheduler::GetCurrentTime(aCurrentTime
);
450 oslDateTime aDateTime
;
451 osl_getDateTimeFromTimeValue(&aCurrentTime
, &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
;
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
)
495 ListenerContainer
aListenerCopy (maListeners
);
498 osl::MutexGuard
aGuard (maMutex
);
500 mbIsCallbackPending
= false;
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: */