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"
22 #include <com/sun/star/lang/XMultiComponentFactory.hpp>
23 #include <com/sun/star/frame/Desktop.hpp>
24 #include <com/sun/star/frame/XTerminateListener.hpp>
26 #include <osl/thread.hxx>
27 #include <osl/conditn.hxx>
34 using namespace ::com::sun::star
;
35 using namespace ::com::sun::star::uno
;
37 namespace sdext
{ namespace presenter
{
44 const PresenterTimer::Task
& rTask
,
45 const TimeValue
& rDueTime
,
46 const sal_Int64 nRepeatInterval
,
47 const sal_Int32 nTaskId
);
49 PresenterTimer::Task
const maTask
;
51 const sal_Int64 mnRepeatInterval
;
52 const sal_Int32 mnTaskId
;
56 typedef std::shared_ptr
<TimerTask
> SharedTimerTask
;
58 class TimerTaskComparator
61 bool operator() (const SharedTimerTask
& rpTask1
, const SharedTimerTask
& rpTask2
) const
63 return rpTask1
->maDueTime
.Seconds
< rpTask2
->maDueTime
.Seconds
64 || (rpTask1
->maDueTime
.Seconds
== rpTask2
->maDueTime
.Seconds
65 && rpTask1
->maDueTime
.Nanosec
< rpTask2
->maDueTime
.Nanosec
);
69 /** Queue all scheduled tasks and process them when their time has come.
72 : public std::enable_shared_from_this
<TimerScheduler
>,
76 static std::shared_ptr
<TimerScheduler
> Instance(
77 uno::Reference
<uno::XComponentContext
> const& xContext
);
78 static SharedTimerTask
CreateTimerTask (
79 const PresenterTimer::Task
& rTask
,
80 const TimeValue
& rDueTime
,
81 const sal_Int64 nRepeatInterval
);
83 void ScheduleTask (const SharedTimerTask
& rpTask
);
84 void CancelTask (const sal_Int32 nTaskId
);
86 static bool GetCurrentTime (TimeValue
& rCurrentTime
);
87 static sal_Int64
GetTimeDifference (
88 const TimeValue
& rTargetTime
,
89 const TimeValue
& rCurrentTime
);
90 static void ConvertToTimeValue (
91 TimeValue
& rTimeValue
,
92 const sal_Int64 nTimeDifference
);
93 static sal_Int64
ConvertFromTimeValue (
94 const TimeValue
& rTimeValue
);
96 static void NotifyTermination();
98 static bool HasInstance() { return mpInstance
!= nullptr; }
102 static std::shared_ptr
<TimerScheduler
> mpInstance
;
103 static ::osl::Mutex maInstanceMutex
;
104 std::shared_ptr
<TimerScheduler
> mpLateDestroy
; // for clean exit
105 static sal_Int32 mnTaskId
;
107 ::osl::Mutex maTaskContainerMutex
;
108 typedef ::std::set
<SharedTimerTask
,TimerTaskComparator
> TaskContainer
;
109 TaskContainer maScheduledTasks
;
110 ::osl::Mutex maCurrentTaskMutex
;
111 SharedTimerTask mpCurrentTask
;
112 ::osl::Condition m_Shutdown
;
115 uno::Reference
<uno::XComponentContext
> const& xContext
);
117 virtual void SAL_CALL
run() override
;
118 virtual void SAL_CALL
onTerminated() override
{ mpLateDestroy
.reset(); }
121 class TerminateListener
122 : public ::cppu::WeakImplHelper
<frame::XTerminateListener
>
124 virtual ~TerminateListener() override
126 assert(!TimerScheduler::HasInstance());
129 virtual void SAL_CALL
disposing(lang::EventObject
const&) override
133 virtual void SAL_CALL
queryTermination(lang::EventObject
const&) override
137 virtual void SAL_CALL
notifyTermination(lang::EventObject
const&) override
139 TimerScheduler::NotifyTermination();
143 } // end of anonymous namespace
145 //===== PresenterTimer ========================================================
147 sal_Int32
PresenterTimer::ScheduleRepeatedTask (
148 const uno::Reference
<uno::XComponentContext
>& xContext
,
150 const sal_Int64 nDelay
,
151 const sal_Int64 nInterval
)
153 assert(xContext
.is());
154 TimeValue aCurrentTime
;
155 if (TimerScheduler::GetCurrentTime(aCurrentTime
))
158 TimerScheduler::ConvertToTimeValue(
160 TimerScheduler::ConvertFromTimeValue (aCurrentTime
) + nDelay
);
161 SharedTimerTask
pTask (TimerScheduler::CreateTimerTask(rTask
, aDueTime
, nInterval
));
162 TimerScheduler::Instance(xContext
)->ScheduleTask(pTask
);
163 return pTask
->mnTaskId
;
166 return NotAValidTaskId
;
169 void PresenterTimer::CancelTask (const sal_Int32 nTaskId
)
171 auto const pInstance(TimerScheduler::Instance(nullptr));
174 pInstance
->CancelTask(nTaskId
);
178 //===== TimerScheduler ========================================================
180 std::shared_ptr
<TimerScheduler
> TimerScheduler::mpInstance
;
181 ::osl::Mutex
TimerScheduler::maInstanceMutex
;
182 sal_Int32
TimerScheduler::mnTaskId
= PresenterTimer::NotAValidTaskId
;
184 std::shared_ptr
<TimerScheduler
> TimerScheduler::Instance(
185 uno::Reference
<uno::XComponentContext
> const& xContext
)
187 ::osl::MutexGuard
aGuard (maInstanceMutex
);
188 if (mpInstance
== nullptr)
192 mpInstance
.reset(new TimerScheduler(xContext
));
193 mpInstance
->create();
198 TimerScheduler::TimerScheduler(
199 uno::Reference
<uno::XComponentContext
> const& xContext
)
200 : maTaskContainerMutex(),
202 maCurrentTaskMutex(),
205 uno::Reference
<frame::XDesktop
> const xDesktop(
206 frame::Desktop::create(xContext
));
207 uno::Reference
<frame::XTerminateListener
> const xListener(
208 new TerminateListener
);
209 // assuming the desktop can take ownership
210 xDesktop
->addTerminateListener(xListener
);
213 SharedTimerTask
TimerScheduler::CreateTimerTask (
214 const PresenterTimer::Task
& rTask
,
215 const TimeValue
& rDueTime
,
216 const sal_Int64 nRepeatInterval
)
218 return std::make_shared
<TimerTask
>(rTask
, rDueTime
, nRepeatInterval
, ++mnTaskId
);
221 void TimerScheduler::ScheduleTask (const SharedTimerTask
& rpTask
)
223 if (rpTask
.get() == nullptr)
225 if (rpTask
->mbIsCanceled
)
229 osl::MutexGuard
aTaskGuard (maTaskContainerMutex
);
230 maScheduledTasks
.insert(rpTask
);
234 void TimerScheduler::CancelTask (const sal_Int32 nTaskId
)
236 // Set of scheduled tasks is sorted after their due times, not their
237 // task ids. Therefore we have to do a linear search for the task to
240 ::osl::MutexGuard
aGuard (maTaskContainerMutex
);
241 auto iTask
= std::find_if(maScheduledTasks
.begin(), maScheduledTasks
.end(),
242 [nTaskId
](const SharedTimerTask
& rxTask
) { return rxTask
->mnTaskId
== nTaskId
; });
243 if (iTask
!= maScheduledTasks
.end())
244 maScheduledTasks
.erase(iTask
);
247 // The task that is to be canceled may be currently about to be
248 // processed. Mark it with a flag that a) prevents a repeating task
249 // from being scheduled again and b) tries to prevent its execution.
251 ::osl::MutexGuard
aGuard (maCurrentTaskMutex
);
252 if (mpCurrentTask
.get() != nullptr
253 && mpCurrentTask
->mnTaskId
== nTaskId
)
254 mpCurrentTask
->mbIsCanceled
= true;
257 // Let the main-loop cleanup in its own time
260 void TimerScheduler::NotifyTermination()
262 std::shared_ptr
<TimerScheduler
> const pInstance(TimerScheduler::mpInstance
);
269 ::osl::MutexGuard
aGuard(pInstance
->maTaskContainerMutex
);
270 pInstance
->maScheduledTasks
.clear();
274 ::osl::MutexGuard
aGuard(pInstance
->maCurrentTaskMutex
);
275 if (pInstance
->mpCurrentTask
)
277 pInstance
->mpCurrentTask
->mbIsCanceled
= true;
281 pInstance
->m_Shutdown
.set();
283 // rhbz#1425304 join thread before shutdown
287 void SAL_CALL
TimerScheduler::run()
289 osl_setThreadName("sdext::presenter::TimerScheduler");
293 // Get the current time.
294 TimeValue aCurrentTime
;
295 if ( ! GetCurrentTime(aCurrentTime
))
297 // We can not get the current time and thus can not schedule anything.
301 // Restrict access to the maScheduledTasks member to one, mutex
303 SharedTimerTask pTask
;
304 sal_Int64 nDifference
= 0;
306 ::osl::MutexGuard
aGuard (maTaskContainerMutex
);
308 // There are no more scheduled task. Leave this loop, function and
309 // live of the TimerScheduler.
310 if (maScheduledTasks
.empty())
313 nDifference
= GetTimeDifference(
314 (*maScheduledTasks
.begin())->maDueTime
,
316 if (nDifference
<= 0)
318 pTask
= *maScheduledTasks
.begin();
319 maScheduledTasks
.erase(maScheduledTasks
.begin());
323 // Acquire a reference to the current task.
325 ::osl::MutexGuard
aGuard (maCurrentTaskMutex
);
326 mpCurrentTask
= pTask
;
329 if (pTask
.get() == nullptr)
331 // Wait until the first task becomes due.
332 TimeValue aTimeValue
;
333 ConvertToTimeValue(aTimeValue
, nDifference
);
334 // wait on condition variable, so the thread can be stopped
335 m_Shutdown
.wait(&aTimeValue
);
340 if (pTask
->maTask
&& !pTask
->mbIsCanceled
)
342 pTask
->maTask(aCurrentTime
);
344 // Re-schedule repeating tasks.
345 if (pTask
->mnRepeatInterval
> 0)
349 ConvertFromTimeValue(pTask
->maDueTime
)
350 + pTask
->mnRepeatInterval
);
357 // Release reference to the current task.
359 ::osl::MutexGuard
aGuard (maCurrentTaskMutex
);
360 mpCurrentTask
.reset();
364 // While holding maInstanceMutex
365 osl::Guard
< osl::Mutex
> aInstance( maInstanceMutex
);
366 mpLateDestroy
= mpInstance
;
370 bool TimerScheduler::GetCurrentTime (TimeValue
& rCurrentTime
)
372 TimeValue aSystemTime
;
373 if (osl_getSystemTime(&aSystemTime
))
374 return osl_getLocalTimeFromSystemTime(&aSystemTime
, &rCurrentTime
);
378 sal_Int64
TimerScheduler::GetTimeDifference (
379 const TimeValue
& rTargetTime
,
380 const TimeValue
& rCurrentTime
)
382 return ConvertFromTimeValue(rTargetTime
) - ConvertFromTimeValue(rCurrentTime
);
385 void TimerScheduler::ConvertToTimeValue (
386 TimeValue
& rTimeValue
,
387 const sal_Int64 nTimeDifference
)
389 rTimeValue
.Seconds
= sal::static_int_cast
<sal_Int32
>(nTimeDifference
/ 1000000000L);
390 rTimeValue
.Nanosec
= sal::static_int_cast
<sal_Int32
>(nTimeDifference
% 1000000000L);
393 sal_Int64
TimerScheduler::ConvertFromTimeValue (
394 const TimeValue
& rTimeValue
)
396 return sal_Int64(rTimeValue
.Seconds
) * 1000000000L + rTimeValue
.Nanosec
;
399 //===== TimerTask =============================================================
403 TimerTask::TimerTask (
404 const PresenterTimer::Task
& rTask
,
405 const TimeValue
& rDueTime
,
406 const sal_Int64 nRepeatInterval
,
407 const sal_Int32 nTaskId
)
410 mnRepeatInterval(nRepeatInterval
),
416 } // end of anonymous namespace
418 //===== PresenterTimer ========================================================
420 ::rtl::Reference
<PresenterClockTimer
> PresenterClockTimer::mpInstance
;
422 ::rtl::Reference
<PresenterClockTimer
> PresenterClockTimer::Instance (
423 const css::uno::Reference
<css::uno::XComponentContext
>& rxContext
)
425 ::osl::MutexGuard
aSolarGuard (::osl::Mutex::getGlobalMutex());
427 ::rtl::Reference
<PresenterClockTimer
> pTimer
;
434 pTimer
.set(new PresenterClockTimer(rxContext
));
440 PresenterClockTimer::PresenterClockTimer (const Reference
<XComponentContext
>& rxContext
)
441 : PresenterClockTimerInterfaceBase(m_aMutex
),
444 mnTimerTaskId(PresenterTimer::NotAValidTaskId
),
445 mbIsCallbackPending(false),
447 , m_xContext(rxContext
)
449 assert(m_xContext
.is());
450 Reference
<lang::XMultiComponentFactory
> xFactory
=
451 rxContext
->getServiceManager();
453 mxRequestCallback
.set(
454 xFactory
->createInstanceWithContext(
455 "com.sun.star.awt.AsyncCallback",
460 PresenterClockTimer::~PresenterClockTimer()
462 if (mnTimerTaskId
!= PresenterTimer::NotAValidTaskId
)
464 PresenterTimer::CancelTask(mnTimerTaskId
);
465 mnTimerTaskId
= PresenterTimer::NotAValidTaskId
;
468 Reference
<lang::XComponent
> xComponent (mxRequestCallback
, UNO_QUERY
);
470 xComponent
->dispose();
471 mxRequestCallback
= nullptr;
474 void PresenterClockTimer::AddListener (const SharedListener
& rListener
)
476 osl::MutexGuard
aGuard (maMutex
);
478 maListeners
.push_back(rListener
);
480 // Create a timer task when the first listener is added.
481 if (mnTimerTaskId
==PresenterTimer::NotAValidTaskId
)
483 mnTimerTaskId
= PresenterTimer::ScheduleRepeatedTask(
485 [this] (TimeValue
const& rTime
) { return this->CheckCurrentTime(rTime
); },
491 void PresenterClockTimer::RemoveListener (const SharedListener
& rListener
)
493 osl::MutexGuard
aGuard (maMutex
);
495 ListenerContainer::iterator
iListener (::std::find(
499 if (iListener
!= maListeners
.end())
500 maListeners
.erase(iListener
);
501 if (maListeners
.empty())
503 // We have no more clients and therefore are not interested in time changes.
504 if (mnTimerTaskId
!= PresenterTimer::NotAValidTaskId
)
506 PresenterTimer::CancelTask(mnTimerTaskId
);
507 mnTimerTaskId
= PresenterTimer::NotAValidTaskId
;
509 mpInstance
= nullptr;
513 oslDateTime
PresenterClockTimer::GetCurrentTime()
515 TimeValue aCurrentTime
;
516 TimerScheduler::GetCurrentTime(aCurrentTime
);
517 oslDateTime aDateTime
;
518 osl_getDateTimeFromTimeValue(&aCurrentTime
, &aDateTime
);
522 void PresenterClockTimer::CheckCurrentTime (const TimeValue
& rCurrentTime
)
524 css::uno::Reference
<css::awt::XRequestCallback
> xRequestCallback
;
525 css::uno::Reference
<css::awt::XCallback
> xCallback
;
527 osl::MutexGuard
aGuard (maMutex
);
529 TimeValue
aCurrentTime (rCurrentTime
);
530 oslDateTime aDateTime
;
531 if (osl_getDateTimeFromTimeValue(&aCurrentTime
, &aDateTime
))
533 if (aDateTime
.Seconds
!= maDateTime
.Seconds
534 || aDateTime
.Minutes
!= maDateTime
.Minutes
535 || aDateTime
.Hours
!= maDateTime
.Hours
)
537 // The displayed part of the current time has changed.
538 // Prepare to call the listeners.
539 maDateTime
= aDateTime
;
541 // Schedule notification of listeners.
542 if (mxRequestCallback
.is() && ! mbIsCallbackPending
)
544 mbIsCallbackPending
= true;
545 xRequestCallback
= mxRequestCallback
;
551 if (xRequestCallback
.is() && xCallback
.is())
552 xRequestCallback
->addCallback(xCallback
, Any());
555 //----- XCallback -------------------------------------------------------------
557 void SAL_CALL
PresenterClockTimer::notify (const css::uno::Any
&)
559 ListenerContainer
aListenerCopy (maListeners
);
562 osl::MutexGuard
aGuard (maMutex
);
564 mbIsCallbackPending
= false;
569 ::std::back_inserter(aListenerCopy
));
572 for (const auto& rxListener
: aListenerCopy
)
574 rxListener
->TimeHasChanged(maDateTime
);
578 } } // end of namespace ::sdext::presenter
580 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */