Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sd / source / console / PresenterTimer.cxx
blobed7be4cbc9c171f774c979022337c0106cc32285
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"
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>
29 #include <algorithm>
30 #include <memory>
31 #include <mutex>
32 #include <set>
33 #include <utility>
35 using namespace ::com::sun::star;
36 using namespace ::com::sun::star::uno;
38 namespace sdext::presenter {
40 namespace {
41 class TimerTask
43 public:
44 TimerTask (
45 PresenterTimer::Task aTask,
46 const TimeValue& rDueTime,
47 const sal_Int64 nRepeatInterval,
48 const sal_Int32 nTaskId);
50 PresenterTimer::Task maTask;
51 TimeValue maDueTime;
52 const sal_Int64 mnRepeatInterval;
53 const sal_Int32 mnTaskId;
54 bool mbIsCanceled;
57 typedef std::shared_ptr<TimerTask> SharedTimerTask;
59 class TimerTaskComparator
61 public:
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.
72 class TimerScheduler
73 : public std::enable_shared_from_this<TimerScheduler>,
74 public ::osl::Thread
76 public:
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();
98 #if !defined NDEBUG
99 static bool HasInstance() { return mpInstance != nullptr; }
100 #endif
102 private:
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;
115 TimerScheduler(
116 uno::Reference<uno::XComponentContext> const& xContext);
117 public:
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,
150 const Task& rTask,
151 const sal_Int64 nDelay,
152 const sal_Int64 nInterval)
154 assert(xContext.is());
155 TimeValue aCurrentTime;
156 if (TimerScheduler::GetCurrentTime(aCurrentTime))
158 TimeValue aDueTime;
159 TimerScheduler::ConvertToTimeValue(
160 aDueTime,
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));
173 if (pInstance)
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)
191 if (!xContext.is())
192 return nullptr;
193 mpInstance.reset(new TimerScheduler(xContext));
194 mpInstance->create();
196 return mpInstance;
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)
220 if (!rpTask)
221 return;
222 if (rpTask->mbIsCanceled)
223 return;
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
235 // cancel.
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);
249 if (mpCurrentTask
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);
260 if (!pInstance)
262 return;
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
281 pInstance->join();
284 void SAL_CALL TimerScheduler::run()
286 osl_setThreadName("sdext::presenter::TimerScheduler");
288 while (true)
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.
295 break;
298 // Restrict access to the maScheduledTasks member to one, mutex
299 // guarded, block.
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())
308 break;
310 nDifference = GetTimeDifference(
311 (*maScheduledTasks.begin())->maDueTime,
312 aCurrentTime);
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;
326 if (!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);
334 else
336 // Execute task.
337 if (pTask->maTask && !pTask->mbIsCanceled)
339 pTask->maTask(aCurrentTime);
341 // Re-schedule repeating tasks.
342 if (pTask->mnRepeatInterval > 0)
344 ConvertToTimeValue(
345 pTask->maDueTime,
346 ConvertFromTimeValue(pTask->maDueTime)
347 + pTask->mnRepeatInterval);
348 ScheduleTask(pTask);
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;
364 mpInstance.reset();
367 bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime)
369 TimeValue aSystemTime;
370 if (osl_getSystemTime(&aSystemTime))
371 return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime);
372 return false;
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 =============================================================
398 namespace {
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)),
406 maDueTime(rDueTime),
407 mnRepeatInterval(nRepeatInterval),
408 mnTaskId(nTaskId),
409 mbIsCanceled(false)
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;
425 if (mpInstance.is())
427 pTimer = mpInstance;
429 if ( ! pTimer.is())
431 pTimer.set(new PresenterClockTimer(rxContext));
432 mpInstance = pTimer;
434 return pTimer;
437 PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext)
438 : PresenterClockTimerInterfaceBase(m_aMutex),
439 maDateTime(),
440 mnTimerTaskId(PresenterTimer::NotAValidTaskId),
441 mbIsCallbackPending(false),
442 m_xContext(rxContext)
444 assert(m_xContext.is());
445 Reference<lang::XMultiComponentFactory> xFactory =
446 rxContext->getServiceManager();
447 if (xFactory.is())
448 mxRequestCallback.set(
449 xFactory->createInstanceWithContext(
450 "com.sun.star.awt.AsyncCallback",
451 rxContext),
452 UNO_QUERY_THROW);
455 PresenterClockTimer::~PresenterClockTimer()
457 if (mnTimerTaskId != PresenterTimer::NotAValidTaskId)
459 PresenterTimer::CancelTask(mnTimerTaskId);
460 mnTimerTaskId = PresenterTimer::NotAValidTaskId;
463 Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY);
464 if (xComponent.is())
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(
479 m_xContext,
480 [this] (TimeValue const& rTime) { return this->CheckCurrentTime(rTime); },
482 250000000 /*ns*/);
486 void PresenterClockTimer::RemoveListener (const SharedListener& rListener)
488 std::unique_lock aGuard (maMutex);
490 ListenerContainer::iterator iListener (::std::find(
491 maListeners.begin(),
492 maListeners.end(),
493 rListener));
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);
514 return 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;
541 xCallback = this;
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: */