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 { operator <(const EventEntry & rEvent) const50 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 EventQueue(boost::shared_ptr<canvas::tools::ElapsedTime> const & pPresTimer)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 ~EventQueue()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 addEvent(const EventSharedPtr & rEvent)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 addEventForNextRound(EventSharedPtr const & rEvent)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 addEventWhenQueueIsEmpty(const EventSharedPtr & rpEvent)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 forceEmpty()169 void EventQueue::forceEmpty() 170 { 171 ::osl::MutexGuard aGuard( maMutex ); 172 173 process_(true); 174 } 175 process()176 void EventQueue::process() 177 { 178 ::osl::MutexGuard aGuard( maMutex ); 179 180 process_(false); 181 } 182 process_(bool bFireAllEvents)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 explicitly 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 explicitly 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 isEmpty() const295 bool EventQueue::isEmpty() const 296 { 297 ::osl::MutexGuard aGuard( maMutex ); 298 299 return maEvents.empty() && maNextEvents.empty() && maNextNextEvents.empty(); 300 } 301 nextTimeout() const302 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 clear()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