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>
35 using namespace ::com::sun::star
;
36 using namespace ::com::sun::star::uno
;
38 namespace sdext::presenter
{
45 PresenterTimer::Task aTask
,
46 const TimeValue
& rDueTime
,
47 const sal_Int64 nRepeatInterval
,
48 const sal_Int32 nTaskId
);
50 PresenterTimer::Task maTask
;
52 const sal_Int64 mnRepeatInterval
;
53 const sal_Int32 mnTaskId
;
57 typedef std::shared_ptr
<TimerTask
> SharedTimerTask
;
59 class TimerTaskComparator
62 bool operator() (const SharedTimerTask
& rpTask1
, const SharedTimerTask
& rpTask2
) const
64 return rpTask1
->maDueTime
.Seconds
< rpTask2
->maDueTime
.Seconds
65 || (rpTask1
->maDueTime
.Seconds
== rpTask2
->maDueTime
.Seconds
66 && rpTask1
->maDueTime
.Nanosec
< rpTask2
->maDueTime
.Nanosec
);
70 /** Queue all scheduled tasks and process them when their time has come.
73 : public std::enable_shared_from_this
<TimerScheduler
>,
77 static std::shared_ptr
<TimerScheduler
> Instance(
78 uno::Reference
<uno::XComponentContext
> const& xContext
);
79 static SharedTimerTask
CreateTimerTask (
80 const PresenterTimer::Task
& rTask
,
81 const TimeValue
& rDueTime
,
82 const sal_Int64 nRepeatInterval
);
84 void ScheduleTask (const SharedTimerTask
& rpTask
);
85 void CancelTask (const sal_Int32 nTaskId
);
87 static bool GetCurrentTime (TimeValue
& rCurrentTime
);
88 static sal_Int64
GetTimeDifference (
89 const TimeValue
& rTargetTime
,
90 const TimeValue
& rCurrentTime
);
91 static void ConvertToTimeValue (
92 TimeValue
& rTimeValue
,
93 const sal_Int64 nTimeDifference
);
94 static sal_Int64
ConvertFromTimeValue (
95 const TimeValue
& rTimeValue
);
97 static void NotifyTermination();
99 static bool HasInstance() { return mpInstance
!= nullptr; }
103 static std::shared_ptr
<TimerScheduler
> mpInstance
;
104 static std::mutex maInstanceMutex
;
105 std::shared_ptr
<TimerScheduler
> mpLateDestroy
; // for clean exit
106 static sal_Int32 mnTaskId
;
108 std::mutex maTaskContainerMutex
;
109 typedef ::std::set
<SharedTimerTask
,TimerTaskComparator
> TaskContainer
;
110 TaskContainer maScheduledTasks
;
111 std::mutex maCurrentTaskMutex
;
112 SharedTimerTask mpCurrentTask
;
113 ::osl::Condition m_Shutdown
;
116 uno::Reference
<uno::XComponentContext
> const& xContext
);
118 virtual void SAL_CALL
run() override
;
119 virtual void SAL_CALL
onTerminated() override
{ mpLateDestroy
.reset(); }
122 class TerminateListener
123 : public ::cppu::WeakImplHelper
<frame::XTerminateListener
>
125 virtual ~TerminateListener() override
127 assert(!TimerScheduler::HasInstance());
130 virtual void SAL_CALL
disposing(lang::EventObject
const&) override
134 virtual void SAL_CALL
queryTermination(lang::EventObject
const&) override
138 virtual void SAL_CALL
notifyTermination(lang::EventObject
const&) override
140 TimerScheduler::NotifyTermination();
144 } // end of anonymous namespace
146 //===== PresenterTimer ========================================================
148 sal_Int32
PresenterTimer::ScheduleRepeatedTask (
149 const uno::Reference
<uno::XComponentContext
>& xContext
,
151 const sal_Int64 nDelay
,
152 const sal_Int64 nInterval
)
154 assert(xContext
.is());
155 TimeValue aCurrentTime
;
156 if (TimerScheduler::GetCurrentTime(aCurrentTime
))
159 TimerScheduler::ConvertToTimeValue(
161 TimerScheduler::ConvertFromTimeValue (aCurrentTime
) + nDelay
);
162 SharedTimerTask
pTask (TimerScheduler::CreateTimerTask(rTask
, aDueTime
, nInterval
));
163 TimerScheduler::Instance(xContext
)->ScheduleTask(pTask
);
164 return pTask
->mnTaskId
;
167 return NotAValidTaskId
;
170 void PresenterTimer::CancelTask (const sal_Int32 nTaskId
)
172 auto const pInstance(TimerScheduler::Instance(nullptr));
175 pInstance
->CancelTask(nTaskId
);
179 //===== TimerScheduler ========================================================
181 std::shared_ptr
<TimerScheduler
> TimerScheduler::mpInstance
;
182 std::mutex
TimerScheduler::maInstanceMutex
;
183 sal_Int32
TimerScheduler::mnTaskId
= PresenterTimer::NotAValidTaskId
;
185 std::shared_ptr
<TimerScheduler
> TimerScheduler::Instance(
186 uno::Reference
<uno::XComponentContext
> const& xContext
)
188 std::scoped_lock
aGuard (maInstanceMutex
);
189 if (mpInstance
== nullptr)
193 mpInstance
.reset(new TimerScheduler(xContext
));
194 mpInstance
->create();
199 TimerScheduler::TimerScheduler(
200 uno::Reference
<uno::XComponentContext
> const& xContext
)
202 uno::Reference
<frame::XDesktop
> const xDesktop(
203 frame::Desktop::create(xContext
));
204 uno::Reference
<frame::XTerminateListener
> const xListener(
205 new TerminateListener
);
206 // assuming the desktop can take ownership
207 xDesktop
->addTerminateListener(xListener
);
210 SharedTimerTask
TimerScheduler::CreateTimerTask (
211 const PresenterTimer::Task
& rTask
,
212 const TimeValue
& rDueTime
,
213 const sal_Int64 nRepeatInterval
)
215 return std::make_shared
<TimerTask
>(rTask
, rDueTime
, nRepeatInterval
, ++mnTaskId
);
218 void TimerScheduler::ScheduleTask (const SharedTimerTask
& rpTask
)
222 if (rpTask
->mbIsCanceled
)
226 std::scoped_lock
aTaskGuard (maTaskContainerMutex
);
227 maScheduledTasks
.insert(rpTask
);
231 void TimerScheduler::CancelTask (const sal_Int32 nTaskId
)
233 // Set of scheduled tasks is sorted after their due times, not their
234 // task ids. Therefore we have to do a linear search for the task to
237 std::scoped_lock
aGuard (maTaskContainerMutex
);
238 auto iTask
= std::find_if(maScheduledTasks
.begin(), maScheduledTasks
.end(),
239 [nTaskId
](const SharedTimerTask
& rxTask
) { return rxTask
->mnTaskId
== nTaskId
; });
240 if (iTask
!= maScheduledTasks
.end())
241 maScheduledTasks
.erase(iTask
);
244 // The task that is to be canceled may be currently about to be
245 // processed. Mark it with a flag that a) prevents a repeating task
246 // from being scheduled again and b) tries to prevent its execution.
248 std::scoped_lock
aGuard (maCurrentTaskMutex
);
250 && mpCurrentTask
->mnTaskId
== nTaskId
)
251 mpCurrentTask
->mbIsCanceled
= true;
254 // Let the main-loop cleanup in its own time
257 void TimerScheduler::NotifyTermination()
259 std::shared_ptr
<TimerScheduler
> const pInstance(TimerScheduler::mpInstance
);
266 std::scoped_lock
aGuard(pInstance
->maTaskContainerMutex
);
267 pInstance
->maScheduledTasks
.clear();
271 std::scoped_lock
aGuard(pInstance
->maCurrentTaskMutex
);
272 if (pInstance
->mpCurrentTask
)
274 pInstance
->mpCurrentTask
->mbIsCanceled
= true;
278 pInstance
->m_Shutdown
.set();
280 // rhbz#1425304 join thread before shutdown
284 void SAL_CALL
TimerScheduler::run()
286 osl_setThreadName("sdext::presenter::TimerScheduler");
290 // Get the current time.
291 TimeValue aCurrentTime
;
292 if ( ! GetCurrentTime(aCurrentTime
))
294 // We can not get the current time and thus can not schedule anything.
298 // Restrict access to the maScheduledTasks member to one, mutex
300 SharedTimerTask pTask
;
301 sal_Int64 nDifference
= 0;
303 std::scoped_lock
aGuard (maTaskContainerMutex
);
305 // There are no more scheduled task. Leave this loop, function and
306 // live of the TimerScheduler.
307 if (maScheduledTasks
.empty())
310 nDifference
= GetTimeDifference(
311 (*maScheduledTasks
.begin())->maDueTime
,
313 if (nDifference
<= 0)
315 pTask
= *maScheduledTasks
.begin();
316 maScheduledTasks
.erase(maScheduledTasks
.begin());
320 // Acquire a reference to the current task.
322 std::scoped_lock
aGuard (maCurrentTaskMutex
);
323 mpCurrentTask
= pTask
;
328 // Wait until the first task becomes due.
329 TimeValue aTimeValue
;
330 ConvertToTimeValue(aTimeValue
, nDifference
);
331 // wait on condition variable, so the thread can be stopped
332 m_Shutdown
.wait(&aTimeValue
);
337 if (pTask
->maTask
&& !pTask
->mbIsCanceled
)
339 pTask
->maTask(aCurrentTime
);
341 // Re-schedule repeating tasks.
342 if (pTask
->mnRepeatInterval
> 0)
346 ConvertFromTimeValue(pTask
->maDueTime
)
347 + pTask
->mnRepeatInterval
);
354 // Release reference to the current task.
356 std::scoped_lock
aGuard (maCurrentTaskMutex
);
357 mpCurrentTask
.reset();
361 // While holding maInstanceMutex
362 std::scoped_lock
aInstance( maInstanceMutex
);
363 mpLateDestroy
= mpInstance
;
367 bool TimerScheduler::GetCurrentTime (TimeValue
& rCurrentTime
)
369 TimeValue aSystemTime
;
370 if (osl_getSystemTime(&aSystemTime
))
371 return osl_getLocalTimeFromSystemTime(&aSystemTime
, &rCurrentTime
);
375 sal_Int64
TimerScheduler::GetTimeDifference (
376 const TimeValue
& rTargetTime
,
377 const TimeValue
& rCurrentTime
)
379 return ConvertFromTimeValue(rTargetTime
) - ConvertFromTimeValue(rCurrentTime
);
382 void TimerScheduler::ConvertToTimeValue (
383 TimeValue
& rTimeValue
,
384 const sal_Int64 nTimeDifference
)
386 rTimeValue
.Seconds
= sal::static_int_cast
<sal_Int32
>(nTimeDifference
/ 1000000000L);
387 rTimeValue
.Nanosec
= sal::static_int_cast
<sal_Int32
>(nTimeDifference
% 1000000000L);
390 sal_Int64
TimerScheduler::ConvertFromTimeValue (
391 const TimeValue
& rTimeValue
)
393 return sal_Int64(rTimeValue
.Seconds
) * 1000000000L + rTimeValue
.Nanosec
;
396 //===== TimerTask =============================================================
400 TimerTask::TimerTask (
401 PresenterTimer::Task aTask
,
402 const TimeValue
& rDueTime
,
403 const sal_Int64 nRepeatInterval
,
404 const sal_Int32 nTaskId
)
405 : maTask(std::move(aTask
)),
407 mnRepeatInterval(nRepeatInterval
),
413 } // end of anonymous namespace
415 //===== PresenterTimer ========================================================
417 ::rtl::Reference
<PresenterClockTimer
> PresenterClockTimer::mpInstance
;
419 ::rtl::Reference
<PresenterClockTimer
> PresenterClockTimer::Instance (
420 const css::uno::Reference
<css::uno::XComponentContext
>& rxContext
)
422 ::osl::MutexGuard
aSolarGuard (::osl::Mutex::getGlobalMutex());
424 ::rtl::Reference
<PresenterClockTimer
> pTimer
;
431 pTimer
.set(new PresenterClockTimer(rxContext
));
437 PresenterClockTimer::PresenterClockTimer (const Reference
<XComponentContext
>& rxContext
)
438 : PresenterClockTimerInterfaceBase(m_aMutex
),
440 mnTimerTaskId(PresenterTimer::NotAValidTaskId
),
441 mbIsCallbackPending(false),
442 m_xContext(rxContext
)
444 assert(m_xContext
.is());
445 Reference
<lang::XMultiComponentFactory
> xFactory
=
446 rxContext
->getServiceManager();
448 mxRequestCallback
.set(
449 xFactory
->createInstanceWithContext(
450 "com.sun.star.awt.AsyncCallback",
455 PresenterClockTimer::~PresenterClockTimer()
457 if (mnTimerTaskId
!= PresenterTimer::NotAValidTaskId
)
459 PresenterTimer::CancelTask(mnTimerTaskId
);
460 mnTimerTaskId
= PresenterTimer::NotAValidTaskId
;
463 Reference
<lang::XComponent
> xComponent (mxRequestCallback
, UNO_QUERY
);
465 xComponent
->dispose();
466 mxRequestCallback
= nullptr;
469 void PresenterClockTimer::AddListener (const SharedListener
& rListener
)
471 std::unique_lock
aGuard (maMutex
);
473 maListeners
.push_back(rListener
);
475 // Create a timer task when the first listener is added.
476 if (mnTimerTaskId
==PresenterTimer::NotAValidTaskId
)
478 mnTimerTaskId
= PresenterTimer::ScheduleRepeatedTask(
480 [this] (TimeValue
const& rTime
) { return this->CheckCurrentTime(rTime
); },
486 void PresenterClockTimer::RemoveListener (const SharedListener
& rListener
)
488 std::unique_lock
aGuard (maMutex
);
490 ListenerContainer::iterator
iListener (::std::find(
494 if (iListener
!= maListeners
.end())
495 maListeners
.erase(iListener
);
496 if (maListeners
.empty())
498 // We have no more clients and therefore are not interested in time changes.
499 if (mnTimerTaskId
!= PresenterTimer::NotAValidTaskId
)
501 PresenterTimer::CancelTask(mnTimerTaskId
);
502 mnTimerTaskId
= PresenterTimer::NotAValidTaskId
;
504 mpInstance
= nullptr;
508 oslDateTime
PresenterClockTimer::GetCurrentTime()
510 TimeValue aCurrentTime
;
511 TimerScheduler::GetCurrentTime(aCurrentTime
);
512 oslDateTime aDateTime
;
513 osl_getDateTimeFromTimeValue(&aCurrentTime
, &aDateTime
);
517 void PresenterClockTimer::CheckCurrentTime (const TimeValue
& rCurrentTime
)
519 css::uno::Reference
<css::awt::XRequestCallback
> xRequestCallback
;
520 css::uno::Reference
<css::awt::XCallback
> xCallback
;
522 std::unique_lock
aGuard (maMutex
);
524 TimeValue
aCurrentTime (rCurrentTime
);
525 oslDateTime aDateTime
;
526 if (osl_getDateTimeFromTimeValue(&aCurrentTime
, &aDateTime
))
528 if (aDateTime
.Seconds
!= maDateTime
.Seconds
529 || aDateTime
.Minutes
!= maDateTime
.Minutes
530 || aDateTime
.Hours
!= maDateTime
.Hours
)
532 // The displayed part of the current time has changed.
533 // Prepare to call the listeners.
534 maDateTime
= aDateTime
;
536 // Schedule notification of listeners.
537 if (mxRequestCallback
.is() && ! mbIsCallbackPending
)
539 mbIsCallbackPending
= true;
540 xRequestCallback
= mxRequestCallback
;
546 if (xRequestCallback
.is() && xCallback
.is())
547 xRequestCallback
->addCallback(xCallback
, Any());
550 //----- XCallback -------------------------------------------------------------
552 void SAL_CALL
PresenterClockTimer::notify (const css::uno::Any
&)
554 ListenerContainer aListenerCopy
;
557 std::unique_lock
aGuard (maMutex
);
559 mbIsCallbackPending
= false;
561 aListenerCopy
= maListeners
;
564 for (const auto& rxListener
: aListenerCopy
)
566 rxListener
->TimeHasChanged(maDateTime
);
570 } // end of namespace ::sdext::presenter
572 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */