Branch data Line data Source code
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 : :
21 : : // must be first
22 : : #include <canvas/debug.hxx>
23 : : #include <tools/diagnose_ex.h>
24 : : #include <canvas/verbosetrace.hxx>
25 : :
26 : : #include <comphelper/anytostring.hxx>
27 : : #include <cppuhelper/exc_hlp.hxx>
28 : :
29 : : #include <event.hxx>
30 : : #include <eventqueue.hxx>
31 : : #include <slideshowexceptions.hxx>
32 : :
33 : : #include <boost/shared_ptr.hpp>
34 : : #include <limits>
35 : :
36 : :
37 : : using namespace ::com::sun::star;
38 : :
39 : : namespace slideshow
40 : : {
41 : : namespace internal
42 : : {
43 : 0 : bool EventQueue::EventEntry::operator<( const EventEntry& rEvent ) const
44 : : {
45 : : // negate comparison, we want priority queue to be sorted
46 : : // in increasing order of activation times
47 : 0 : return this->nTime > rEvent.nTime;
48 : : }
49 : :
50 : :
51 : 0 : EventQueue::EventQueue(
52 : : boost::shared_ptr<canvas::tools::ElapsedTime> const & pPresTimer )
53 : : : maMutex(),
54 : : maEvents(),
55 : : maNextEvents(),
56 : : maNextNextEvents(),
57 : 0 : mpTimer( pPresTimer )
58 : : {
59 : 0 : }
60 : :
61 : 0 : EventQueue::~EventQueue()
62 : : {
63 : : // add in all that have been added explicitly for this round:
64 : 0 : EventEntryVector::const_iterator const iEnd( maNextEvents.end() );
65 : 0 : for ( EventEntryVector::const_iterator iPos( maNextEvents.begin() );
66 : : iPos != iEnd; ++iPos )
67 : : {
68 : 0 : maEvents.push(*iPos);
69 : : }
70 : 0 : EventEntryVector().swap( maNextEvents );
71 : :
72 : : // dispose event queue
73 : 0 : while( !maEvents.empty() )
74 : : {
75 : : try
76 : : {
77 : 0 : maEvents.top().pEvent->dispose();
78 : : }
79 : 0 : catch (uno::Exception &)
80 : : {
81 : : OSL_FAIL( rtl::OUStringToOString(
82 : : comphelper::anyToString(
83 : : cppu::getCaughtException() ),
84 : : RTL_TEXTENCODING_UTF8 ).getStr() );
85 : : }
86 : 0 : maEvents.pop();
87 : : }
88 : 0 : }
89 : :
90 : 0 : bool EventQueue::addEvent( const EventSharedPtr& rEvent )
91 : : {
92 : 0 : ::osl::MutexGuard aGuard( maMutex );
93 : :
94 : : #if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS)
95 : : OSL_TRACE("adding at %f event [%s] at %x with delay %f\r",
96 : : mpTimer->getElapsedTime(),
97 : : OUStringToOString(rEvent->GetDescription(), RTL_TEXTENCODING_UTF8).getStr(),
98 : : rEvent.get(),
99 : : rEvent->getActivationTime(0.0));
100 : : #endif
101 : 0 : ENSURE_OR_RETURN_FALSE( rEvent,
102 : : "EventQueue::addEvent: event ptr NULL" );
103 : :
104 : : // prepare entry
105 : :
106 : : // A seemingly obvious optimization cannot be used here,
107 : : // because it breaks assumed order of notification: zero
108 : : // timeout events could be fired() immediately, but that
109 : : // would not unwind the stack and furthermore changes
110 : : // order of notification
111 : :
112 : : // add entry
113 : 0 : maEvents.push( EventEntry( rEvent, rEvent->getActivationTime(
114 : 0 : mpTimer->getElapsedTime()) ) );
115 : 0 : return true;
116 : : }
117 : :
118 : 0 : bool EventQueue::addEventForNextRound( EventSharedPtr const& rEvent )
119 : : {
120 : 0 : ::osl::MutexGuard aGuard( maMutex );
121 : :
122 : : #if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS)
123 : : OSL_TRACE("adding at %f event [%s] at %x for next round with delay %f\r",
124 : : mpTimer->getElapsedTime(),
125 : : OUStringToOString(rEvent->GetDescription(), RTL_TEXTENCODING_UTF8).getStr(),
126 : : rEvent.get(),
127 : : rEvent->getActivationTime(0.0));
128 : : #endif
129 : :
130 : 0 : ENSURE_OR_RETURN_FALSE( rEvent.get() != NULL,
131 : : "EventQueue::addEvent: event ptr NULL" );
132 : : maNextEvents.push_back(
133 : 0 : EventEntry( rEvent, rEvent->getActivationTime(
134 : 0 : mpTimer->getElapsedTime()) ) );
135 : 0 : return true;
136 : : }
137 : :
138 : 0 : bool EventQueue::addEventWhenQueueIsEmpty (const EventSharedPtr& rpEvent)
139 : : {
140 : 0 : ::osl::MutexGuard aGuard( maMutex );
141 : :
142 : : #if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS)
143 : : OSL_TRACE("adding at %f event [%s] at %x for execution when queue is empty with delay %f\r",
144 : : mpTimer->getElapsedTime(),
145 : : OUStringToOString(rpEvent->GetDescription(), RTL_TEXTENCODING_UTF8).getStr(),
146 : : rpEvent.get(),
147 : : rpEvent->getActivationTime(0.0));
148 : : #endif
149 : :
150 : 0 : ENSURE_OR_RETURN_FALSE(
151 : : rpEvent.get() != NULL,
152 : : "EventQueue::addEvent: event ptr NULL");
153 : :
154 : : maNextNextEvents.push(
155 : : EventEntry(
156 : : rpEvent,
157 : 0 : rpEvent->getActivationTime(mpTimer->getElapsedTime())));
158 : :
159 : 0 : return true;
160 : : }
161 : :
162 : 0 : void EventQueue::forceEmpty()
163 : : {
164 : 0 : ::osl::MutexGuard aGuard( maMutex );
165 : :
166 : 0 : process_(true);
167 : 0 : }
168 : :
169 : 0 : void EventQueue::process()
170 : : {
171 : 0 : ::osl::MutexGuard aGuard( maMutex );
172 : :
173 : 0 : process_(false);
174 : 0 : }
175 : :
176 : 0 : void EventQueue::process_( bool bFireAllEvents )
177 : : {
178 : : VERBOSE_TRACE( "EventQueue: heartbeat" );
179 : :
180 : : // add in all that have been added explicitly for this round:
181 : 0 : EventEntryVector::const_iterator const iEnd( maNextEvents.end() );
182 : 0 : for ( EventEntryVector::const_iterator iPos( maNextEvents.begin() );
183 : : iPos != iEnd; ++iPos ) {
184 : 0 : maEvents.push(*iPos);
185 : : }
186 : 0 : EventEntryVector().swap( maNextEvents );
187 : :
188 : : // perform topmost, ready-to-execute event
189 : : // =======================================
190 : :
191 : 0 : const double nCurrTime( mpTimer->getElapsedTime() );
192 : :
193 : : // When maEvents does not contain any events that are due now
194 : : // then process one event from maNextNextEvents.
195 : 0 : if (!maNextNextEvents.empty()
196 : 0 : && !bFireAllEvents
197 : 0 : && (maEvents.empty() || maEvents.top().nTime > nCurrTime))
198 : : {
199 : 0 : const EventEntry aEvent (maNextNextEvents.top());
200 : 0 : maNextNextEvents.pop();
201 : 0 : maEvents.push(aEvent);
202 : : }
203 : :
204 : : // process ready/elapsed events. Note that the 'perceived'
205 : : // current time remains constant for this loop, thus we're
206 : : // processing only those events which where ready when we
207 : : // entered this method.
208 : 0 : while( !maEvents.empty() &&
209 : 0 : (bFireAllEvents || maEvents.top().nTime <= nCurrTime) )
210 : : {
211 : 0 : EventEntry event( maEvents.top() );
212 : 0 : maEvents.pop();
213 : :
214 : : // only process event, if it is still 'charged',
215 : : // i.e. the fire() call effects something. This is
216 : : // used when e.g. having events registered at multiple
217 : : // places, which should fire only once: after the
218 : : // initial fire() call, those events become inactive
219 : : // and return false on isCharged. This frees us from
220 : : // the need to prune queues of those inactive shells.
221 : 0 : if( event.pEvent->isCharged() )
222 : : {
223 : : try
224 : : {
225 : : #if OSL_DEBUG_LEVEL > 0
226 : : VERBOSE_TRACE( "Firing event: unknown (0x%X), timeout was: %f",
227 : : event.pEvent.get(),
228 : : event.pEvent->getActivationTime(0.0) );
229 : : #endif
230 : : #if OSL_DEBUG_LEVEL > 1 && defined (SLIDESHOW_ADD_DESCRIPTIONS_TO_EVENTS)
231 : : OSL_TRACE("firing at %f event [%s] at %x with delay %f\r",
232 : : mpTimer->getElapsedTime(),
233 : : OUStringToOString(event.pEvent->GetDescription(),
234 : : RTL_TEXTENCODING_UTF8).getStr(),
235 : : event.pEvent.get(),
236 : : event.pEvent->getActivationTime(0.0));
237 : : #endif
238 : :
239 : 0 : event.pEvent->fire();
240 : : }
241 : 0 : catch( uno::RuntimeException& )
242 : : {
243 : 0 : throw;
244 : : }
245 : 0 : catch( uno::Exception& )
246 : : {
247 : : // catch anything here, we don't want
248 : : // to leave this scope under _any_
249 : : // circumstance. Although, do _not_
250 : : // reinsert an activity that threw
251 : : // once.
252 : :
253 : : // NOTE: we explicitly don't catch(...) here,
254 : : // since this will also capture segmentation
255 : : // violations and the like. In such a case, we
256 : : // still better let our clients now...
257 : : OSL_FAIL( rtl::OUStringToOString(
258 : : comphelper::anyToString( cppu::getCaughtException() ),
259 : : RTL_TEXTENCODING_UTF8 ).getStr() );
260 : : }
261 : 0 : catch( SlideShowException& )
262 : : {
263 : : // catch anything here, we don't want
264 : : // to leave this scope under _any_
265 : : // circumstance. Although, do _not_
266 : : // reinsert an activity that threw
267 : : // once.
268 : :
269 : : // NOTE: we explicitly don't catch(...) here,
270 : : // since this will also capture segmentation
271 : : // violations and the like. In such a case, we
272 : : // still better let our clients now...
273 : : OSL_TRACE( "::presentation::internal::EventQueue: Event threw a SlideShowException, action might not have been fully performed" );
274 : : }
275 : : }
276 : : else
277 : : {
278 : : #if OSL_DEBUG_LEVEL > 0
279 : : VERBOSE_TRACE( "Ignoring discharged event: unknown (0x%X), timeout was: %f",
280 : : event.pEvent.get(),
281 : : event.pEvent->getActivationTime(0.0) );
282 : : #endif
283 : : }
284 : 0 : }
285 : 0 : }
286 : :
287 : 0 : bool EventQueue::isEmpty() const
288 : : {
289 : 0 : ::osl::MutexGuard aGuard( maMutex );
290 : :
291 : 0 : return maEvents.empty() && maNextEvents.empty() && maNextNextEvents.empty();
292 : : }
293 : :
294 : 0 : double EventQueue::nextTimeout() const
295 : : {
296 : 0 : ::osl::MutexGuard aGuard( maMutex );
297 : :
298 : : // return time for next entry (if any)
299 : 0 : double nTimeout (::std::numeric_limits<double>::max());
300 : 0 : const double nCurrentTime (mpTimer->getElapsedTime());
301 : 0 : if ( ! maEvents.empty())
302 : 0 : nTimeout = maEvents.top().nTime - nCurrentTime;
303 : 0 : if ( ! maNextEvents.empty())
304 : 0 : nTimeout = ::std::min(nTimeout, maNextEvents.front().nTime - nCurrentTime);
305 : 0 : if ( ! maNextNextEvents.empty())
306 : 0 : nTimeout = ::std::min(nTimeout, maNextNextEvents.top().nTime - nCurrentTime);
307 : :
308 : 0 : return nTimeout;
309 : : }
310 : :
311 : 0 : void EventQueue::clear()
312 : : {
313 : 0 : ::osl::MutexGuard aGuard( maMutex );
314 : :
315 : : // TODO(P1): Maybe a plain vector and vector.swap will
316 : : // be faster here. Profile.
317 : 0 : maEvents = ImplQueueType();
318 : :
319 : 0 : maNextEvents.clear();
320 : 0 : maNextNextEvents = ImplQueueType();
321 : 0 : }
322 : : }
323 : : }
324 : :
325 : : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|