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