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