LibreOffice Module sd (master) 1
PresenterTimer.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include "PresenterTimer.hxx"
21
22#include <com/sun/star/lang/XMultiComponentFactory.hpp>
23#include <com/sun/star/frame/Desktop.hpp>
24#include <com/sun/star/frame/XTerminateListener.hpp>
25
26#include <osl/thread.hxx>
27#include <osl/conditn.hxx>
28
29#include <algorithm>
30#include <memory>
31#include <mutex>
32#include <set>
33#include <utility>
34
35using namespace ::com::sun::star;
36using namespace ::com::sun::star::uno;
37
38namespace sdext::presenter {
39
40namespace {
41class TimerTask
42{
43public:
44 TimerTask (
46 const TimeValue& rDueTime,
47 const sal_Int64 nRepeatInterval,
48 const sal_Int32 nTaskId);
49
51 TimeValue maDueTime;
52 const sal_Int64 mnRepeatInterval;
53 const sal_Int32 mnTaskId;
55};
56
57typedef std::shared_ptr<TimerTask> SharedTimerTask;
58
59class TimerTaskComparator
60{
61public:
62 bool operator() (const SharedTimerTask& rpTask1, const SharedTimerTask& rpTask2) const
63 {
64 return rpTask1->maDueTime.Seconds < rpTask2->maDueTime.Seconds
65 || (rpTask1->maDueTime.Seconds == rpTask2->maDueTime.Seconds
66 && rpTask1->maDueTime.Nanosec < rpTask2->maDueTime.Nanosec);
67 }
68};
69
72class TimerScheduler
73 : public std::enable_shared_from_this<TimerScheduler>,
74 public ::osl::Thread
75{
76public:
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);
83
84 void ScheduleTask (const SharedTimerTask& rpTask);
85 void CancelTask (const sal_Int32 nTaskId);
86
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);
96
97 static void NotifyTermination();
98#if !defined NDEBUG
99 static bool HasInstance() { return mpInstance != nullptr; }
100#endif
101
102private:
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;
107
109 typedef ::std::set<SharedTimerTask,TimerTaskComparator> TaskContainer;
110 TaskContainer maScheduledTasks;
112 SharedTimerTask mpCurrentTask;
113 ::osl::Condition m_Shutdown;
114
115 TimerScheduler(
116 uno::Reference<uno::XComponentContext> const& xContext);
117public:
118 virtual void SAL_CALL run() override;
119 virtual void SAL_CALL onTerminated() override { mpLateDestroy.reset(); }
120};
121
122class TerminateListener
123 : public ::cppu::WeakImplHelper<frame::XTerminateListener>
124{
125 virtual ~TerminateListener() override
126 {
127 assert(!TimerScheduler::HasInstance());
128 }
129
130 virtual void SAL_CALL disposing(lang::EventObject const&) override
131 {
132 }
133
134 virtual void SAL_CALL queryTermination(lang::EventObject const&) override
135 {
136 }
137
138 virtual void SAL_CALL notifyTermination(lang::EventObject const&) override
139 {
140 TimerScheduler::NotifyTermination();
141 }
142};
143
144} // end of anonymous namespace
145
146//===== PresenterTimer ========================================================
147
149 const uno::Reference<uno::XComponentContext>& xContext,
150 const Task& rTask,
151 const sal_Int64 nDelay,
152 const sal_Int64 nInterval)
153{
154 assert(xContext.is());
155 TimeValue aCurrentTime;
156 if (TimerScheduler::GetCurrentTime(aCurrentTime))
157 {
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;
165 }
166
167 return NotAValidTaskId;
168}
169
170void PresenterTimer::CancelTask (const sal_Int32 nTaskId)
171{
172 auto const pInstance(TimerScheduler::Instance(nullptr));
173 if (pInstance)
174 {
175 pInstance->CancelTask(nTaskId);
176 }
177}
178
179//===== TimerScheduler ========================================================
180
181std::shared_ptr<TimerScheduler> TimerScheduler::mpInstance;
184
185std::shared_ptr<TimerScheduler> TimerScheduler::Instance(
186 uno::Reference<uno::XComponentContext> const& xContext)
187{
188 std::scoped_lock aGuard (maInstanceMutex);
189 if (mpInstance == nullptr)
190 {
191 if (!xContext.is())
192 return nullptr;
193 mpInstance.reset(new TimerScheduler(xContext));
194 mpInstance->create();
195 }
196 return mpInstance;
197}
198
199TimerScheduler::TimerScheduler(
200 uno::Reference<uno::XComponentContext> const& xContext)
201{
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);
208}
209
210SharedTimerTask TimerScheduler::CreateTimerTask (
211 const PresenterTimer::Task& rTask,
212 const TimeValue& rDueTime,
213 const sal_Int64 nRepeatInterval)
214{
215 return std::make_shared<TimerTask>(rTask, rDueTime, nRepeatInterval, ++mnTaskId);
216}
217
218void TimerScheduler::ScheduleTask (const SharedTimerTask& rpTask)
219{
220 if (!rpTask)
221 return;
222 if (rpTask->mbIsCanceled)
223 return;
224
225 {
226 std::scoped_lock aTaskGuard (maTaskContainerMutex);
227 maScheduledTasks.insert(rpTask);
228 }
229}
230
231void TimerScheduler::CancelTask (const sal_Int32 nTaskId)
232{
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.
236 {
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);
242 }
243
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.
247 {
248 std::scoped_lock aGuard (maCurrentTaskMutex);
249 if (mpCurrentTask
250 && mpCurrentTask->mnTaskId == nTaskId)
251 mpCurrentTask->mbIsCanceled = true;
252 }
253
254 // Let the main-loop cleanup in its own time
255}
256
257void TimerScheduler::NotifyTermination()
258{
259 std::shared_ptr<TimerScheduler> const pInstance(TimerScheduler::mpInstance);
260 if (!pInstance)
261 {
262 return;
263 }
264
265 {
266 std::scoped_lock aGuard(pInstance->maTaskContainerMutex);
267 pInstance->maScheduledTasks.clear();
268 }
269
270 {
271 std::scoped_lock aGuard(pInstance->maCurrentTaskMutex);
272 if (pInstance->mpCurrentTask)
273 {
274 pInstance->mpCurrentTask->mbIsCanceled = true;
275 }
276 }
277
278 pInstance->m_Shutdown.set();
279
280 // rhbz#1425304 join thread before shutdown
281 pInstance->join();
282}
283
284void SAL_CALL TimerScheduler::run()
285{
286 osl_setThreadName("sdext::presenter::TimerScheduler");
287
288 while (true)
289 {
290 // Get the current time.
291 TimeValue aCurrentTime;
292 if ( ! GetCurrentTime(aCurrentTime))
293 {
294 // We can not get the current time and thus can not schedule anything.
295 break;
296 }
297
298 // Restrict access to the maScheduledTasks member to one, mutex
299 // guarded, block.
300 SharedTimerTask pTask;
301 sal_Int64 nDifference = 0;
302 {
303 std::scoped_lock aGuard (maTaskContainerMutex);
304
305 // There are no more scheduled task. Leave this loop, function and
306 // live of the TimerScheduler.
307 if (maScheduledTasks.empty())
308 break;
309
310 nDifference = GetTimeDifference(
311 (*maScheduledTasks.begin())->maDueTime,
312 aCurrentTime);
313 if (nDifference <= 0)
314 {
315 pTask = *maScheduledTasks.begin();
316 maScheduledTasks.erase(maScheduledTasks.begin());
317 }
318 }
319
320 // Acquire a reference to the current task.
321 {
322 std::scoped_lock aGuard (maCurrentTaskMutex);
323 mpCurrentTask = pTask;
324 }
325
326 if (!pTask)
327 {
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);
333 }
334 else
335 {
336 // Execute task.
337 if (pTask->maTask && !pTask->mbIsCanceled)
338 {
339 pTask->maTask(aCurrentTime);
340
341 // Re-schedule repeating tasks.
342 if (pTask->mnRepeatInterval > 0)
343 {
344 ConvertToTimeValue(
345 pTask->maDueTime,
346 ConvertFromTimeValue(pTask->maDueTime)
347 + pTask->mnRepeatInterval);
348 ScheduleTask(pTask);
349 }
350 }
351
352 }
353
354 // Release reference to the current task.
355 {
356 std::scoped_lock aGuard (maCurrentTaskMutex);
357 mpCurrentTask.reset();
358 }
359 }
360
361 // While holding maInstanceMutex
362 std::scoped_lock aInstance( maInstanceMutex );
364 mpInstance.reset();
365}
366
367bool TimerScheduler::GetCurrentTime (TimeValue& rCurrentTime)
368{
369 TimeValue aSystemTime;
370 if (osl_getSystemTime(&aSystemTime))
371 return osl_getLocalTimeFromSystemTime(&aSystemTime, &rCurrentTime);
372 return false;
373}
374
375sal_Int64 TimerScheduler::GetTimeDifference (
376 const TimeValue& rTargetTime,
377 const TimeValue& rCurrentTime)
378{
379 return ConvertFromTimeValue(rTargetTime) - ConvertFromTimeValue(rCurrentTime);
380}
381
382void TimerScheduler::ConvertToTimeValue (
383 TimeValue& rTimeValue,
384 const sal_Int64 nTimeDifference)
385{
386 rTimeValue.Seconds = sal::static_int_cast<sal_Int32>(nTimeDifference / 1000000000L);
387 rTimeValue.Nanosec = sal::static_int_cast<sal_Int32>(nTimeDifference % 1000000000L);
388}
389
390sal_Int64 TimerScheduler::ConvertFromTimeValue (
391 const TimeValue& rTimeValue)
392{
393 return sal_Int64(rTimeValue.Seconds) * 1000000000L + rTimeValue.Nanosec;
394}
395
396//===== TimerTask =============================================================
397
398namespace {
399
400TimerTask::TimerTask (
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)
410{
411}
412
413} // end of anonymous namespace
414
415//===== PresenterTimer ========================================================
416
418
420 const css::uno::Reference<css::uno::XComponentContext>& rxContext)
421{
422 ::osl::MutexGuard aSolarGuard (::osl::Mutex::getGlobalMutex());
423
425 if (mpInstance.is())
426 {
427 pTimer = mpInstance;
428 }
429 if ( ! pTimer.is())
430 {
431 pTimer.set(new PresenterClockTimer(rxContext));
432 mpInstance = pTimer;
433 }
434 return pTimer;
435}
436
437PresenterClockTimer::PresenterClockTimer (const Reference<XComponentContext>& rxContext)
439 maDateTime(),
440 mnTimerTaskId(PresenterTimer::NotAValidTaskId),
441 mbIsCallbackPending(false),
442 m_xContext(rxContext)
443{
444 assert(m_xContext.is());
445 Reference<lang::XMultiComponentFactory> xFactory =
446 rxContext->getServiceManager();
447 if (xFactory.is())
449 xFactory->createInstanceWithContext(
450 "com.sun.star.awt.AsyncCallback",
451 rxContext),
452 UNO_QUERY_THROW);
453}
454
456{
458 {
461 }
462
463 Reference<lang::XComponent> xComponent (mxRequestCallback, UNO_QUERY);
464 if (xComponent.is())
465 xComponent->dispose();
466 mxRequestCallback = nullptr;
467}
468
470{
471 std::unique_lock aGuard (maMutex);
472
473 maListeners.push_back(rListener);
474
475 // Create a timer task when the first listener is added.
477 {
480 [this] (TimeValue const& rTime) { return this->CheckCurrentTime(rTime); },
481 0,
482 250000000 /*ns*/);
483 }
484}
485
487{
488 std::unique_lock aGuard (maMutex);
489
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())
497 {
498 // We have no more clients and therefore are not interested in time changes.
500 {
503 }
504 mpInstance = nullptr;
505 }
506}
507
509{
510 TimeValue aCurrentTime;
511 TimerScheduler::GetCurrentTime(aCurrentTime);
512 oslDateTime aDateTime;
513 osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime);
514 return aDateTime;
515}
516
517void PresenterClockTimer::CheckCurrentTime (const TimeValue& rCurrentTime)
518{
519 css::uno::Reference<css::awt::XRequestCallback> xRequestCallback;
520 css::uno::Reference<css::awt::XCallback> xCallback;
521 {
522 std::unique_lock aGuard (maMutex);
523
524 TimeValue aCurrentTime (rCurrentTime);
525 oslDateTime aDateTime;
526 if (osl_getDateTimeFromTimeValue(&aCurrentTime, &aDateTime))
527 {
528 if (aDateTime.Seconds != maDateTime.Seconds
529 || aDateTime.Minutes != maDateTime.Minutes
530 || aDateTime.Hours != maDateTime.Hours)
531 {
532 // The displayed part of the current time has changed.
533 // Prepare to call the listeners.
534 maDateTime = aDateTime;
535
536 // Schedule notification of listeners.
538 {
539 mbIsCallbackPending = true;
540 xRequestCallback = mxRequestCallback;
541 xCallback = this;
542 }
543 }
544 }
545 }
546 if (xRequestCallback.is() && xCallback.is())
547 xRequestCallback->addCallback(xCallback, Any());
548}
549
550//----- XCallback -------------------------------------------------------------
551
552void SAL_CALL PresenterClockTimer::notify (const css::uno::Any&)
553{
554 ListenerContainer aListenerCopy;
555
556 {
557 std::unique_lock aGuard (maMutex);
558
559 mbIsCallbackPending = false;
560
561 aListenerCopy = maListeners;
562 }
563
564 for (const auto& rxListener : aListenerCopy)
565 {
566 rxListener->TimeHasChanged(maDateTime);
567 }
568}
569
570} // end of namespace ::sdext::presenter
571
572/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XComponentContext > m_xContext
::osl::Condition m_Shutdown
std::mutex maCurrentTaskMutex
static std::shared_ptr< TimerScheduler > mpInstance
const sal_Int32 mnTaskId
static std::mutex maInstanceMutex
std::shared_ptr< TimerScheduler > mpLateDestroy
PresenterTimer::Task maTask
TaskContainer maScheduledTasks
SharedTimerTask mpCurrentTask
TimeValue maDueTime
std::mutex maTaskContainerMutex
const sal_Int64 mnRepeatInterval
bool mbIsCanceled
css::uno::Reference< css::awt::XRequestCallback > mxRequestCallback
::std::vector< SharedListener > ListenerContainer
virtual void SAL_CALL notify(const css::uno::Any &rUserData) override
static ::rtl::Reference< PresenterClockTimer > Instance(const css::uno::Reference< css::uno::XComponentContext > &rxContext)
static ::rtl::Reference< PresenterClockTimer > mpInstance
std::shared_ptr< Listener > SharedListener
const css::uno::Reference< css::uno::XComponentContext > m_xContext
PresenterClockTimer(const css::uno::Reference< css::uno::XComponentContext > &rxContext)
void AddListener(const SharedListener &rListener)
void CheckCurrentTime(const TimeValue &rCurrentTime)
void RemoveListener(const SharedListener &rListener)
The timer allows tasks to be scheduled for execution at a specified time in the future.
static void CancelTask(const sal_Int32 nTaskId)
::std::function< void(const TimeValue &)> Task
A task is called with the current time.
static const sal_Int32 NotAValidTaskId
static sal_Int32 ScheduleRepeatedTask(const css::uno::Reference< css::uno::XComponentContext > &xContext, const Task &rTask, const sal_Int64 nFirst, const sal_Int64 nInterval)
Schedule a task to be executed repeatedly.
Reference< XSingleServiceFactory > xFactory
std::mutex m_aMutex
def run(arg=None, arg2=-1)
cppu::WeakComponentImplHelper< css::awt::XCallback > PresenterClockTimerInterfaceBase
DateTime maDateTime