diff options
-rw-r--r-- | Swiften/EventLoop/Cocoa/CocoaEvent.h | 4 | ||||
-rw-r--r-- | Swiften/EventLoop/Cocoa/CocoaEvent.mm | 13 | ||||
-rw-r--r-- | Swiften/EventLoop/Cocoa/CocoaEventLoop.h | 14 | ||||
-rw-r--r-- | Swiften/EventLoop/Cocoa/CocoaEventLoop.mm | 34 | ||||
-rw-r--r-- | Swiften/EventLoop/DummyEventLoop.cpp | 22 | ||||
-rw-r--r-- | Swiften/EventLoop/DummyEventLoop.h | 42 | ||||
-rw-r--r-- | Swiften/EventLoop/EventLoop.cpp | 77 | ||||
-rw-r--r-- | Swiften/EventLoop/EventLoop.h | 41 | ||||
-rw-r--r-- | Swiften/EventLoop/Qt/QtEventLoop.h | 32 | ||||
-rw-r--r-- | Swiften/EventLoop/SimpleEventLoop.cpp | 40 | ||||
-rw-r--r-- | Swiften/EventLoop/SimpleEventLoop.h | 14 | ||||
-rw-r--r-- | Swiften/EventLoop/SingleThreadedEventLoop.cpp | 37 | ||||
-rw-r--r-- | Swiften/EventLoop/SingleThreadedEventLoop.h | 12 |
13 files changed, 206 insertions, 176 deletions
diff --git a/Swiften/EventLoop/Cocoa/CocoaEvent.h b/Swiften/EventLoop/Cocoa/CocoaEvent.h index dc38f7f..4453c74 100644 --- a/Swiften/EventLoop/Cocoa/CocoaEvent.h +++ b/Swiften/EventLoop/Cocoa/CocoaEvent.h @@ -9,7 +9,6 @@ #include <Foundation/Foundation.h> namespace Swift { - class Event; class CocoaEventLoop; } @@ -19,14 +18,13 @@ namespace Swift { #pragma clang diagnostic ignored "-Wobjc-interface-ivars" @interface CocoaEvent : NSObject { - Swift::Event* event; Swift::CocoaEventLoop* eventLoop; } #pragma clang diagnostic pop // Takes ownership of event -- (id) initWithEvent: (Swift::Event*) e eventLoop: (Swift::CocoaEventLoop*) el; +- (id) init:(Swift::CocoaEventLoop*) el; - (void) process; - (void) dealloc; diff --git a/Swiften/EventLoop/Cocoa/CocoaEvent.mm b/Swiften/EventLoop/Cocoa/CocoaEvent.mm index 7b1b4b0..4f72c29 100644 --- a/Swiften/EventLoop/Cocoa/CocoaEvent.mm +++ b/Swiften/EventLoop/Cocoa/CocoaEvent.mm @@ -1,24 +1,27 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #include <Swiften/EventLoop/Cocoa/CocoaEvent.h> -#include <Swiften/EventLoop/Event.h> #include <Swiften/EventLoop/Cocoa/CocoaEventLoop.h> @implementation CocoaEvent -- (id) initWithEvent: (Swift::Event*) e eventLoop: (Swift::CocoaEventLoop*) el { +- (id) init:(Swift::CocoaEventLoop*) el { self = [super init]; if (self != nil) { - event = e; eventLoop = el; } return self; } - (void) process { - eventLoop->handleEvent(*event); + eventLoop->handleNextCocoaEvent(); } - (void) dealloc { - delete event; [super dealloc]; } diff --git a/Swiften/EventLoop/Cocoa/CocoaEventLoop.h b/Swiften/EventLoop/Cocoa/CocoaEventLoop.h index ee33fbb..aad6b0a 100644 --- a/Swiften/EventLoop/Cocoa/CocoaEventLoop.h +++ b/Swiften/EventLoop/Cocoa/CocoaEventLoop.h @@ -1,20 +1,28 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once +#include <boost/thread.hpp> + #include <Swiften/EventLoop/EventLoop.h> namespace Swift { class CocoaEventLoop : public EventLoop { public: CocoaEventLoop(); + virtual ~CocoaEventLoop(); - virtual void post(const Event& event); + void handleNextCocoaEvent(); + + protected: + virtual void eventPosted(); - using EventLoop::handleEvent; + private: + bool isEventInCocoaEventLoop_; + boost::recursive_mutex isEventInCocoaEventLoopMutex_; }; } diff --git a/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm b/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm index ba73884..88da262 100644 --- a/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm +++ b/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2015 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #include <Swiften/EventLoop/Cocoa/CocoaEventLoop.h> #include <Swiften/EventLoop/Cocoa/CocoaEvent.h> @@ -5,17 +11,33 @@ namespace Swift { -CocoaEventLoop::CocoaEventLoop() { +CocoaEventLoop::CocoaEventLoop() : isEventInCocoaEventLoop_(false) { +} + +CocoaEventLoop::~CocoaEventLoop() { + +} + +void CocoaEventLoop::handleNextCocoaEvent() { + { + boost::recursive_mutex::scoped_lock lock(isEventInCocoaEventLoopMutex_); + isEventInCocoaEventLoop_ = false; + } + handleNextEvent(); } -void CocoaEventLoop::post(const Event& event) { - Event* eventCopy = new Event(event); - CocoaEvent* cocoaEvent = [[CocoaEvent alloc] initWithEvent: eventCopy eventLoop: this]; - [cocoaEvent +void CocoaEventLoop::eventPosted() { + boost::recursive_mutex::scoped_lock lock(isEventInCocoaEventLoopMutex_); + if (!isEventInCocoaEventLoop_) { + isEventInCocoaEventLoop_ = true; + + CocoaEvent* cocoaEvent = [[CocoaEvent alloc] init: this]; + [cocoaEvent performSelectorOnMainThread:@selector(process) withObject: nil waitUntilDone: NO]; - [cocoaEvent release]; + [cocoaEvent release]; + } } } diff --git a/Swiften/EventLoop/DummyEventLoop.cpp b/Swiften/EventLoop/DummyEventLoop.cpp index b8e631e..3675ead 100644 --- a/Swiften/EventLoop/DummyEventLoop.cpp +++ b/Swiften/EventLoop/DummyEventLoop.cpp @@ -10,16 +10,30 @@ namespace Swift { -DummyEventLoop::DummyEventLoop() { +DummyEventLoop::DummyEventLoop() : hasEvents_(false) { } DummyEventLoop::~DummyEventLoop() { - boost::lock_guard<boost::mutex> lock(eventsMutex_); - if (!events_.empty()) { + if (hasEvents()) { std::cerr << "DummyEventLoop: Unhandled events at destruction time" << std::endl; } - events_.clear(); } +void DummyEventLoop::processEvents() { + while(hasEvents()) { + hasEvents_ = false; + handleNextEvent(); + } +} + +bool DummyEventLoop::hasEvents() { + boost::lock_guard<boost::mutex> lock(hasEventsMutex_); + return hasEvents_; +} + +void DummyEventLoop::eventPosted() { + boost::lock_guard<boost::mutex> lock(hasEventsMutex_); + hasEvents_ = true; +} } diff --git a/Swiften/EventLoop/DummyEventLoop.h b/Swiften/EventLoop/DummyEventLoop.h index 297549e..b41cd09 100644 --- a/Swiften/EventLoop/DummyEventLoop.h +++ b/Swiften/EventLoop/DummyEventLoop.h @@ -8,8 +8,8 @@ #include <deque> -#include <boost/thread/mutex.hpp> #include <boost/thread/locks.hpp> +#include <boost/thread/mutex.hpp> #include <Swiften/Base/API.h> #include <Swiften/EventLoop/EventLoop.h> @@ -20,40 +20,14 @@ namespace Swift { DummyEventLoop(); virtual ~DummyEventLoop(); - void processEvents() { - while (hasEvents()) { - /* - Creating a copy of the to-be-handled Event object because handling - it can result in a DummyEventLoop::post() call. - This call would also try to lock the eventsMutex_, resulting in a - deadlock. - */ - - eventsMutex_.lock(); - Event eventCopy = events_[0]; - eventsMutex_.unlock(); - - handleEvent(eventCopy); - - { - boost::lock_guard<boost::mutex> lock(eventsMutex_); - events_.pop_front(); - } - } - } - - bool hasEvents() { - boost::lock_guard<boost::mutex> lock(eventsMutex_); - return !events_.empty(); - } - - virtual void post(const Event& event) { - boost::lock_guard<boost::mutex> lock(eventsMutex_); - events_.push_back(event); - } + void processEvents(); + + bool hasEvents(); + + virtual void eventPosted(); private: - boost::mutex eventsMutex_; - std::deque<Event> events_; + bool hasEvents_; + boost::mutex hasEventsMutex_; }; } diff --git a/Swiften/EventLoop/EventLoop.cpp b/Swiften/EventLoop/EventLoop.cpp index 0b50e82..d9a9081 100644 --- a/Swiften/EventLoop/EventLoop.cpp +++ b/Swiften/EventLoop/EventLoop.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -7,11 +7,12 @@ #include <Swiften/EventLoop/EventLoop.h> #include <algorithm> -#include <iostream> #include <cassert> + #include <boost/bind.hpp> -#include <boost/lambda/lambda.hpp> #include <boost/lambda/bind.hpp> +#include <boost/lambda/lambda.hpp> +#include <boost/optional.hpp> #include <boost/thread/locks.hpp> #include <Swiften/Base/Log.h> @@ -26,68 +27,66 @@ inline void invokeCallback(const Event& event) { event.callback(); } catch (const std::exception& e) { - std::cerr << "Uncaught exception in event loop: " << e.what() << std::endl; + SWIFT_LOG(error) << "Uncaught exception in event loop: " << e.what() << std::endl; } catch (...) { - std::cerr << "Uncaught non-exception in event loop" << std::endl; + SWIFT_LOG(error) << "Uncaught non-exception in event loop" << std::endl; } } -EventLoop::EventLoop() : nextEventID_(0), handlingEvents_(false) { +EventLoop::EventLoop() : nextEventID_(0) { } EventLoop::~EventLoop() { } -void EventLoop::handleEvent(const Event& event) { - //SWIFT_LOG(debug) << "Handling event " << event.id << std::endl; - - if (handlingEvents_) { - // We're being called recursively. Push in the list of events to - // handle in the parent handleEvent() - eventsToHandle_.push_back(event); - return; - } - - bool doCallback = false; +void EventLoop::handleNextEvent() { + bool callEventPosted = false; { - boost::lock_guard<boost::mutex> lock(eventsMutex_); - std::list<Event>::iterator i = std::find(events_.begin(), events_.end(), event); - if (i != events_.end()) { - doCallback = true; - events_.erase(i); + boost::recursive_mutex::scoped_lock lock(removeEventsMutex_); + { + boost::optional<Event> nextEvent; + { + boost::recursive_mutex::scoped_lock lock(eventsMutex_); + if (!events_.empty()) { + nextEvent = events_.front(); + events_.pop_front(); + } + } + callEventPosted = !events_.empty(); + if (nextEvent) { + invokeCallback(nextEvent.get()); + } } + } - if (doCallback) { - handlingEvents_ = true; - invokeCallback(event); - - // Process events that were passed to handleEvent during the callback - // (i.e. through recursive calls of handleEvent) - while (!eventsToHandle_.empty()) { - Event nextEvent = eventsToHandle_.front(); - eventsToHandle_.pop_front(); - invokeCallback(nextEvent); - } - handlingEvents_ = false; + + if (callEventPosted) { + eventPosted(); } } void EventLoop::postEvent(boost::function<void ()> callback, boost::shared_ptr<EventOwner> owner) { Event event(owner, callback); + bool callEventPosted = false; { - boost::lock_guard<boost::mutex> lock(eventsMutex_); + boost::recursive_mutex::scoped_lock lock(eventsMutex_); + + callEventPosted = events_.empty(); + event.id = nextEventID_; nextEventID_++; events_.push_back(event); } - //SWIFT_LOG(debug) << "Posting event " << event.id << std::endl; - post(event); + if (callEventPosted) { + eventPosted(); + } } void EventLoop::removeEventsFromOwner(boost::shared_ptr<EventOwner> owner) { - boost::lock_guard<boost::mutex> lock(eventsMutex_); - events_.remove_if(lambda::bind(&Event::owner, lambda::_1) == owner); + boost::recursive_mutex::scoped_lock removeLock(removeEventsMutex_); + boost::recursive_mutex::scoped_lock lock(eventsMutex_); + events_.remove_if(lambda::bind(&Event::owner, lambda::_1) == owner); } } diff --git a/Swiften/EventLoop/EventLoop.h b/Swiften/EventLoop/EventLoop.h index e64ff35..54e0fe7 100644 --- a/Swiften/EventLoop/EventLoop.h +++ b/Swiften/EventLoop/EventLoop.h @@ -1,15 +1,15 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #pragma once +#include <list> + #include <boost/function.hpp> #include <boost/thread.hpp> -#include <list> -#include <deque> #include <Swiften/Base/API.h> #include <Swiften/EventLoop/Event.h> @@ -17,28 +17,49 @@ namespace Swift { class EventOwner; + /** + * The \ref EventLoop class provides the abstract interface for implementing event loops to use with Swiften. + * + * Events are added to the event queue using the \ref postEvent method and can be removed from the queue using + * the \ref removeEventsFromOwner method. + */ class SWIFTEN_API EventLoop { public: EventLoop(); virtual ~EventLoop(); + /** + * The \ref postEvent method allows events to be added to the event queue of the \ref EventLoop. + * An optional \ref EventOwner can be passed, allowing later removal of events that have not yet been + * executed using the \ref removeEventsFromOwner method. + */ void postEvent(boost::function<void ()> event, boost::shared_ptr<EventOwner> owner = boost::shared_ptr<EventOwner>()); + + /** + * The \ref removeEventsFromOwner method removes all events from the specified \ref owner from the + * event queue. + */ void removeEventsFromOwner(boost::shared_ptr<EventOwner> owner); protected: /** - * Reimplement this to call handleEvent(event) from the thread in which - * the event loop is residing. + * The \ref handleNextEvent method is called by an implementation of the abstract \ref EventLoop class + * at any point after the virtual \ref eventPosted method has been called. + * This method does not block, except for short-time synchronization. */ - virtual void post(const Event& event) = 0; + void handleNextEvent(); - void handleEvent(const Event& event); + /** + * The \ref eventPosted virtual method serves as notification for when events are still available in the queue. + * It is called after the first event is posted to an empty queue or after an event has been handled in + * \ref handleNextEvent and there are still remaining events in the queue. + */ + virtual void eventPosted() = 0; private: - boost::mutex eventsMutex_; unsigned int nextEventID_; std::list<Event> events_; - bool handlingEvents_; - std::deque<Event> eventsToHandle_; + boost::recursive_mutex eventsMutex_; + boost::recursive_mutex removeEventsMutex_; }; } diff --git a/Swiften/EventLoop/Qt/QtEventLoop.h b/Swiften/EventLoop/Qt/QtEventLoop.h index 0e048e5..389b0a7 100644 --- a/Swiften/EventLoop/Qt/QtEventLoop.h +++ b/Swiften/EventLoop/Qt/QtEventLoop.h @@ -6,28 +6,39 @@ #pragma once -#include <QObject> -#include <QEvent> +#include <boost/thread.hpp> + #include <QCoreApplication> +#include <QEvent> +#include <QObject> #include <Swiften/EventLoop/EventLoop.h> namespace Swift { class QtEventLoop : public QObject, public EventLoop { public: - QtEventLoop() {} + QtEventLoop() : isEventInQtEventLoop_(false) {} virtual ~QtEventLoop() { QCoreApplication::removePostedEvents(this); } - virtual void post(const Swift::Event& event) { - QCoreApplication::postEvent(this, new Event(event)); + protected: + virtual void eventPosted() { + boost::recursive_mutex::scoped_lock lock(isEventInQtEventLoopMutex_); + if (!isEventInQtEventLoop_) { + isEventInQtEventLoop_ = true; + QCoreApplication::postEvent(this, new Event()); + } } virtual bool event(QEvent* qevent) { Event* event = dynamic_cast<Event*>(qevent); if (event) { - handleEvent(event->event_); + { + boost::recursive_mutex::scoped_lock lock(isEventInQtEventLoopMutex_); + isEventInQtEventLoop_ = false; + } + handleNextEvent(); //event->deleteLater(); FIXME: Leak? return true; } @@ -37,11 +48,12 @@ namespace Swift { private: struct Event : public QEvent { - Event(const Swift::Event& event) : - QEvent(QEvent::User), event_(event) { + Event() : + QEvent(QEvent::User) { } - - Swift::Event event_; }; + + bool isEventInQtEventLoop_; + boost::recursive_mutex isEventInQtEventLoopMutex_; }; } diff --git a/Swiften/EventLoop/SimpleEventLoop.cpp b/Swiften/EventLoop/SimpleEventLoop.cpp index 09b84dc..0bc06c9 100644 --- a/Swiften/EventLoop/SimpleEventLoop.cpp +++ b/Swiften/EventLoop/SimpleEventLoop.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 Isode Limited. + * Copyright (c) 2010-2015 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ @@ -7,35 +7,28 @@ #include <Swiften/EventLoop/SimpleEventLoop.h> #include <boost/bind.hpp> -#include <iostream> #include <Swiften/Base/foreach.h> - namespace Swift { -SimpleEventLoop::SimpleEventLoop() : isRunning_(true) { +SimpleEventLoop::SimpleEventLoop() : isRunning_(true), eventAvailable_(false) { } SimpleEventLoop::~SimpleEventLoop() { - if (!events_.empty()) { - std::cerr << "Warning: Pending events in SimpleEventLoop at destruction time" << std::endl; - } } void SimpleEventLoop::doRun(bool breakAfterEvents) { while (isRunning_) { - std::vector<Event> events; { - boost::unique_lock<boost::mutex> lock(eventsMutex_); - while (events_.empty()) { - eventsAvailable_.wait(lock); + boost::unique_lock<boost::mutex> lock(eventAvailableMutex_); + while (!eventAvailable_) { + eventAvailableCondition_.wait(lock); } - events.swap(events_); - } - foreach(const Event& event, events) { - handleEvent(event); + + eventAvailable_ = false; } + runOnce(); if (breakAfterEvents) { return; } @@ -43,14 +36,7 @@ void SimpleEventLoop::doRun(bool breakAfterEvents) { } void SimpleEventLoop::runOnce() { - std::vector<Event> events; - { - boost::unique_lock<boost::mutex> lock(eventsMutex_); - events.swap(events_); - } - foreach(const Event& event, events) { - handleEvent(event); - } + handleNextEvent(); } void SimpleEventLoop::stop() { @@ -61,12 +47,12 @@ void SimpleEventLoop::doStop() { isRunning_ = false; } -void SimpleEventLoop::post(const Event& event) { +void SimpleEventLoop::eventPosted() { { - boost::lock_guard<boost::mutex> lock(eventsMutex_); - events_.push_back(event); + boost::unique_lock<boost::mutex> lock(eventAvailableMutex_); + eventAvailable_ = true; } - eventsAvailable_.notify_one(); + eventAvailableCondition_.notify_one(); } diff --git a/Swiften/EventLoop/SimpleEventLoop.h b/Swiften/EventLoop/SimpleEventLoop.h index eedaf88..221591e 100644 --- a/Swiften/EventLoop/SimpleEventLoop.h +++ b/Swiften/EventLoop/SimpleEventLoop.h @@ -6,8 +6,6 @@ #pragma once -#include <vector> -#include <boost/function.hpp> #include <boost/thread/mutex.hpp> #include <boost/thread/condition_variable.hpp> @@ -31,8 +29,9 @@ namespace Swift { void runOnce(); void stop(); - - virtual void post(const Event& event); + + protected: + virtual void eventPosted(); private: void doRun(bool breakAfterEvents); @@ -40,8 +39,9 @@ namespace Swift { private: bool isRunning_; - std::vector<Event> events_; - boost::mutex eventsMutex_; - boost::condition_variable eventsAvailable_; + + bool eventAvailable_; + boost::mutex eventAvailableMutex_; + boost::condition_variable eventAvailableCondition_; }; } diff --git a/Swiften/EventLoop/SingleThreadedEventLoop.cpp b/Swiften/EventLoop/SingleThreadedEventLoop.cpp index c2235b1..095b962 100644 --- a/Swiften/EventLoop/SingleThreadedEventLoop.cpp +++ b/Swiften/EventLoop/SingleThreadedEventLoop.cpp @@ -15,20 +15,18 @@ namespace Swift { SingleThreadedEventLoop::SingleThreadedEventLoop() -: shouldShutDown_(false) +: shouldShutDown_(false), eventAvailable_(false) { } SingleThreadedEventLoop::~SingleThreadedEventLoop() { - if (!events_.empty()) { - std::cerr << "Warning: Pending events in SingleThreadedEventLoop at destruction time." << std::endl; - } + } void SingleThreadedEventLoop::waitForEvents() { - boost::unique_lock<boost::mutex> lock(eventsMutex_); - while (events_.empty() && !shouldShutDown_) { - eventsAvailable_.wait(lock); + boost::unique_lock<boost::mutex> lock(eventAvailableMutex_); + while (!eventAvailable_ && !shouldShutDown_) { + eventAvailableCondition_.wait(lock); } if (shouldShutDown_) @@ -36,30 +34,23 @@ void SingleThreadedEventLoop::waitForEvents() { } void SingleThreadedEventLoop::handleEvents() { - // Make a copy of the list of events so we don't block any threads that post - // events while we process them. - std::vector<Event> events; { - boost::unique_lock<boost::mutex> lock(eventsMutex_); - events.swap(events_); - } - - // Loop through all the events and handle them - foreach(const Event& event, events) { - handleEvent(event); + boost::lock_guard<boost::mutex> lock(eventAvailableMutex_); + eventAvailable_ = false; } + handleNextEvent(); } void SingleThreadedEventLoop::stop() { - boost::unique_lock<boost::mutex> lock(eventsMutex_); + boost::unique_lock<boost::mutex> lock(eventAvailableMutex_); shouldShutDown_ = true; - eventsAvailable_.notify_one(); + eventAvailableCondition_.notify_one(); } -void SingleThreadedEventLoop::post(const Event& event) { - boost::lock_guard<boost::mutex> lock(eventsMutex_); - events_.push_back(event); - eventsAvailable_.notify_one(); +void SingleThreadedEventLoop::eventPosted() { + boost::lock_guard<boost::mutex> lock(eventAvailableMutex_); + eventAvailable_ = true; + eventAvailableCondition_.notify_one(); } } // namespace Swift diff --git a/Swiften/EventLoop/SingleThreadedEventLoop.h b/Swiften/EventLoop/SingleThreadedEventLoop.h index 75ffad0..2145d652 100644 --- a/Swiften/EventLoop/SingleThreadedEventLoop.h +++ b/Swiften/EventLoop/SingleThreadedEventLoop.h @@ -39,7 +39,7 @@ namespace Swift { public: SingleThreadedEventLoop(); - ~SingleThreadedEventLoop(); + virtual ~SingleThreadedEventLoop(); // Blocks while waiting for new events and returns when new events are available. // Throws EventLoopCanceledException when the wait is canceled. @@ -47,12 +47,14 @@ namespace Swift { void handleEvents(); void stop(); - virtual void post(const Event& event); + protected: + virtual void eventPosted(); private: bool shouldShutDown_; - std::vector<Event> events_; - boost::mutex eventsMutex_; - boost::condition_variable eventsAvailable_; + + bool eventAvailable_; + boost::mutex eventAvailableMutex_; + boost::condition_variable eventAvailableCondition_; }; } |