diff options
Diffstat (limited to 'slideshow/source/engine/eventqueue.cxx')
-rw-r--r-- | slideshow/source/engine/eventqueue.cxx | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/slideshow/source/engine/eventqueue.cxx b/slideshow/source/engine/eventqueue.cxx new file mode 100644 index 000000000..b63db471d --- /dev/null +++ b/slideshow/source/engine/eventqueue.cxx @@ -0,0 +1,295 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include <tools/diagnose_ex.h> +#include <sal/log.hxx> + +#include <event.hxx> +#include <eventqueue.hxx> +#include <slideshowexceptions.hxx> + +#include <limits> +#include <memory> + + +using namespace ::com::sun::star; + +namespace slideshow::internal +{ + bool EventQueue::EventEntry::operator<( const EventEntry& rEvent ) const + { + // negate comparison, we want priority queue to be sorted + // in increasing order of activation times + return nTime > rEvent.nTime; + } + + + EventQueue::EventQueue( + std::shared_ptr<canvas::tools::ElapsedTime> const & pPresTimer ) + : maMutex(), + maEvents(), + maNextEvents(), + maNextNextEvents(), + mpTimer( pPresTimer ) + { + } + + EventQueue::~EventQueue() + { + // add in all that have been added explicitly for this round: + for ( const auto& rEvent : maNextEvents ) + { + maEvents.push(rEvent); + } + EventEntryVector().swap( maNextEvents ); + + // dispose event queue + while( !maEvents.empty() ) + { + try + { + maEvents.top().pEvent->dispose(); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("slideshow", ""); + } + maEvents.pop(); + } + } + + bool EventQueue::addEvent( const EventSharedPtr& rEvent ) + { + std::unique_lock aGuard( maMutex ); + + SAL_INFO("slideshow.eventqueue", "adding event \"" << rEvent->GetDescription() + << "\" [" << rEvent.get() + << "] at " << mpTimer->getElapsedTime() + << " with delay " << rEvent->getActivationTime(0.0) + ); + ENSURE_OR_RETURN_FALSE( rEvent, + "EventQueue::addEvent: event ptr NULL" ); + + // prepare entry + + // A seemingly obvious optimization cannot be used here, + // because it breaks assumed order of notification: zero + // timeout events could be fired() immediately, but that + // would not unwind the stack and furthermore changes + // order of notification + + // add entry + maEvents.push( EventEntry( rEvent, rEvent->getActivationTime( + mpTimer->getElapsedTime()) ) ); + return true; + } + + bool EventQueue::addEventForNextRound( EventSharedPtr const& rEvent ) + { + std::unique_lock aGuard( maMutex ); + + SAL_INFO("slideshow.eventqueue", "adding event \"" << rEvent->GetDescription() + << "\" [" << rEvent.get() + << "] for the next round at " << mpTimer->getElapsedTime() + << " with delay " << rEvent->getActivationTime(0.0) + ); + + ENSURE_OR_RETURN_FALSE( rEvent, + "EventQueue::addEvent: event ptr NULL" ); + maNextEvents.emplace_back( rEvent, rEvent->getActivationTime( + mpTimer->getElapsedTime()) ); + return true; + } + + bool EventQueue::addEventWhenQueueIsEmpty (const EventSharedPtr& rpEvent) + { + std::unique_lock aGuard( maMutex ); + + SAL_INFO("slideshow.eventqueue", "adding event \"" << rpEvent->GetDescription() + << "\" [" << rpEvent.get() + << "] for execution when the queue is empty at " << mpTimer->getElapsedTime() + << " with delay " << rpEvent->getActivationTime(0.0) + ); + + ENSURE_OR_RETURN_FALSE( rpEvent, "EventQueue::addEvent: event ptr NULL"); + + maNextNextEvents.push( + EventEntry( + rpEvent, + rpEvent->getActivationTime(mpTimer->getElapsedTime()))); + + return true; + } + + void EventQueue::forceEmpty() + { + process_(true); + } + + void EventQueue::process() + { + process_(false); + } + + void EventQueue::process_( bool bFireAllEvents ) + { + std::unique_lock aGuard( maMutex ); + + SAL_INFO("slideshow.verbose", "EventQueue: heartbeat" ); + + // add in all that have been added explicitly for this round: + for ( const auto& rEvent : maNextEvents ) { + maEvents.push(rEvent); + } + EventEntryVector().swap( maNextEvents ); + + // perform topmost, ready-to-execute event + // ======================================= + + const double nCurrTime( mpTimer->getElapsedTime() ); + + // When maEvents does not contain any events that are due now + // then process one event from maNextNextEvents. + if (!maNextNextEvents.empty() + && !bFireAllEvents + && (maEvents.empty() || maEvents.top().nTime > nCurrTime)) + { + const EventEntry aEvent (std::move(maNextNextEvents.top())); + maNextNextEvents.pop(); + maEvents.push(aEvent); + } + + // process ready/elapsed events. Note that the 'perceived' + // current time remains constant for this loop, thus we're + // processing only those events which where ready when we + // entered this method. + while( !maEvents.empty() && + (bFireAllEvents || maEvents.top().nTime <= nCurrTime) ) + { + EventEntry event( std::move(maEvents.top()) ); + maEvents.pop(); + + // only process event, if it is still 'charged', + // i.e. the fire() call effects something. This is + // used when e.g. having events registered at multiple + // places, which should fire only once: after the + // initial fire() call, those events become inactive + // and return false on isCharged. This frees us from + // the need to prune queues of those inactive shells. + if( event.pEvent->isCharged() ) + { + aGuard.unlock(); + try + { + SAL_INFO("slideshow.eventqueue", "firing event \"" + << event.pEvent->GetDescription() + << "\" [" << event.pEvent.get() + << "] at " << mpTimer->getElapsedTime() + << " with delay " << event.pEvent->getActivationTime(0.0) + ); + event.pEvent->fire(); + SAL_INFO("slideshow.eventqueue", "event \"" + << event.pEvent->GetDescription() + << "\" [" << event.pEvent.get() << "] fired" + ); + } + catch( uno::RuntimeException& ) + { + throw; + } + catch( uno::Exception& ) + { + // catch anything here, we don't want + // to leave this scope under _any_ + // circumstance. Although, do _not_ + // reinsert an activity that threw + // once. + + // NOTE: we explicitly don't catch(...) here, + // since this will also capture segmentation + // violations and the like. In such a case, we + // still better let our clients now... + TOOLS_WARN_EXCEPTION( "slideshow", "" ); + } + catch( SlideShowException& ) + { + // catch anything here, we don't want + // to leave this scope under _any_ + // circumstance. Although, do _not_ + // reinsert an activity that threw + // once. + + // NOTE: we explicitly don't catch(...) here, + // since this will also capture segmentation + // violations and the like. In such a case, we + // still better let our clients now... + SAL_WARN("slideshow.eventqueue", "::presentation::internal::EventQueue: Event threw a SlideShowException, action might not have been fully performed" ); + } + aGuard.lock(); + } + else + { + SAL_INFO( + "slideshow.eventqueue", + "Ignoring discharged event: unknown (" + << event.pEvent.get() << "), timeout was: " + << event.pEvent->getActivationTime(0.0)); + } + } + } + + bool EventQueue::isEmpty() const + { + std::unique_lock aGuard( maMutex ); + + return maEvents.empty() && maNextEvents.empty() && maNextNextEvents.empty(); + } + + double EventQueue::nextTimeout() const + { + std::unique_lock aGuard( maMutex ); + + // return time for next entry (if any) + double nTimeout (::std::numeric_limits<double>::max()); + const double nCurrentTime (mpTimer->getElapsedTime()); + if ( ! maEvents.empty()) + nTimeout = maEvents.top().nTime - nCurrentTime; + if ( ! maNextEvents.empty()) + nTimeout = ::std::min(nTimeout, maNextEvents.front().nTime - nCurrentTime); + if ( ! maNextNextEvents.empty()) + nTimeout = ::std::min(nTimeout, maNextNextEvents.top().nTime - nCurrentTime); + + return nTimeout; + } + + void EventQueue::clear() + { + std::unique_lock aGuard( maMutex ); + + // TODO(P1): Maybe a plain vector and vector.swap will + // be faster here. Profile. + maEvents = ImplQueueType(); + + maNextEvents.clear(); + maNextNextEvents = ImplQueueType(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |