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