diff options
Diffstat (limited to 'Swiften/EventLoop')
22 files changed, 684 insertions, 524 deletions
diff --git a/Swiften/EventLoop/BoostASIOEventLoop.cpp b/Swiften/EventLoop/BoostASIOEventLoop.cpp new file mode 100644 index 0000000..45dd4a2 --- /dev/null +++ b/Swiften/EventLoop/BoostASIOEventLoop.cpp @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2015-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#include <Swiften/EventLoop/BoostASIOEventLoop.h> + +#include <boost/bind.hpp> + +namespace Swift { + +BoostASIOEventLoop::BoostASIOEventLoop(std::shared_ptr<boost::asio::io_service> ioService) : ioService_(ioService) { + +} + +BoostASIOEventLoop::~BoostASIOEventLoop() { + +} + +void BoostASIOEventLoop::handleASIOEvent() { + { + std::unique_lock<std::recursive_mutex> lock(isEventInASIOEventLoopMutex_); + isEventInASIOEventLoop_ = false; + } + handleNextEvent(); +} + +void BoostASIOEventLoop::eventPosted() { + std::unique_lock<std::recursive_mutex> lock(isEventInASIOEventLoopMutex_); + if (!isEventInASIOEventLoop_) { + isEventInASIOEventLoop_ = true; + ioService_->post(boost::bind(&BoostASIOEventLoop::handleASIOEvent, this)); + } +} + +} diff --git a/Swiften/EventLoop/BoostASIOEventLoop.h b/Swiften/EventLoop/BoostASIOEventLoop.h new file mode 100644 index 0000000..fbdf443 --- /dev/null +++ b/Swiften/EventLoop/BoostASIOEventLoop.h @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + +#pragma once + +#include <memory> +#include <mutex> + +#include <boost/asio/io_service.hpp> + +#include <Swiften/Base/API.h> +#include <Swiften/EventLoop/Event.h> +#include <Swiften/EventLoop/EventLoop.h> + +namespace Swift { + class SWIFTEN_API BoostASIOEventLoop : public EventLoop { + public: + BoostASIOEventLoop(std::shared_ptr<boost::asio::io_service> ioService); + virtual ~BoostASIOEventLoop(); + + protected: + void handleASIOEvent(); + + virtual void eventPosted(); + + private: + std::shared_ptr<boost::asio::io_service> ioService_; + + bool isEventInASIOEventLoop_ = false; + std::recursive_mutex isEventInASIOEventLoopMutex_; + }; +} diff --git a/Swiften/EventLoop/Cocoa/CocoaEvent.h b/Swiften/EventLoop/Cocoa/CocoaEvent.h index 89d056f..945056e 100644 --- a/Swiften/EventLoop/Cocoa/CocoaEvent.h +++ b/Swiften/EventLoop/Cocoa/CocoaEvent.h @@ -1,16 +1,19 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once #include <Foundation/Foundation.h> +// The following line is a workaround for a bug in Boost 1.60 when building as C++11. +// See ticket #11897 and #11863 in Boost's bug tracker. +#undef check + namespace Swift { - class Event; - class CocoaEventLoop; + class CocoaEventLoop; } // Using deprecated declaration of instance vars in interface, because this @@ -19,14 +22,13 @@ namespace Swift { #pragma clang diagnostic ignored "-Wobjc-interface-ivars" @interface CocoaEvent : NSObject { - Swift::Event* event; - Swift::CocoaEventLoop* eventLoop; + 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..fc9695b 100644 --- a/Swiften/EventLoop/Cocoa/CocoaEvent.mm +++ b/Swiften/EventLoop/Cocoa/CocoaEvent.mm @@ -1,25 +1,29 @@ +/* + * Copyright (c) 2015-2016 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 { - self = [super init]; - if (self != nil) { - event = e; - eventLoop = el; - } - return self; +- (id) init:(Swift::CocoaEventLoop*) el { + self = [super init]; + if (self != nil) { + eventLoop = el; + } + return self; } - (void) process { - eventLoop->handleEvent(*event); + eventLoop->handleNextCocoaEvent(); } - (void) dealloc { - delete event; - [super dealloc]; + [super dealloc]; } @end diff --git a/Swiften/EventLoop/Cocoa/CocoaEventLoop.h b/Swiften/EventLoop/Cocoa/CocoaEventLoop.h index 60ef32b..7f20e6c 100644 --- a/Swiften/EventLoop/Cocoa/CocoaEventLoop.h +++ b/Swiften/EventLoop/Cocoa/CocoaEventLoop.h @@ -1,20 +1,28 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once +#include <mutex> + #include <Swiften/EventLoop/EventLoop.h> namespace Swift { - class CocoaEventLoop : public EventLoop { - public: - CocoaEventLoop(); + class CocoaEventLoop : public EventLoop { + public: + CocoaEventLoop(); + virtual ~CocoaEventLoop(); + + void handleNextCocoaEvent(); - virtual void post(const Event& event); + protected: + virtual void eventPosted(); - using EventLoop::handleEvent; - }; + private: + bool isEventInCocoaEventLoop_; + std::recursive_mutex isEventInCocoaEventLoopMutex_; + }; } diff --git a/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm b/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm index ba73884..39dc7ec 100644 --- a/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm +++ b/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2015-2019 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::post(const Event& event) { - Event* eventCopy = new Event(event); - CocoaEvent* cocoaEvent = [[CocoaEvent alloc] initWithEvent: eventCopy eventLoop: this]; - [cocoaEvent - performSelectorOnMainThread:@selector(process) - withObject: nil - waitUntilDone: NO]; - [cocoaEvent release]; +void CocoaEventLoop::handleNextCocoaEvent() { + { + std::unique_lock<std::recursive_mutex> lock(isEventInCocoaEventLoopMutex_); + isEventInCocoaEventLoop_ = false; + } + handleNextEvent(); +} + +void CocoaEventLoop::eventPosted() { + std::unique_lock<std::recursive_mutex> lock(isEventInCocoaEventLoopMutex_); + if (!isEventInCocoaEventLoop_) { + isEventInCocoaEventLoop_ = true; + + CocoaEvent* cocoaEvent = [[CocoaEvent alloc] init: this]; + [cocoaEvent + performSelectorOnMainThread:@selector(process) + withObject: nil + waitUntilDone: NO]; + [cocoaEvent release]; + } } } diff --git a/Swiften/EventLoop/DummyEventLoop.cpp b/Swiften/EventLoop/DummyEventLoop.cpp index 3741eec..234ba7a 100644 --- a/Swiften/EventLoop/DummyEventLoop.cpp +++ b/Swiften/EventLoop/DummyEventLoop.cpp @@ -1,12 +1,12 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/EventLoop/DummyEventLoop.h> -#include <iostream> +#include <Swiften/Base/Log.h> namespace Swift { @@ -14,11 +14,24 @@ DummyEventLoop::DummyEventLoop() { } DummyEventLoop::~DummyEventLoop() { - if (!events_.empty()) { - std::cerr << "DummyEventLoop: Unhandled events at destruction time" << std::endl; - } - events_.clear(); + if (hasEvents()) { + SWIFT_LOG(warning) << "DummyEventLoop: Unhandled events at destruction time"; + } } +void DummyEventLoop::processEvents() { + while(hasEvents()) { + hasEvents_ = false; + handleNextEvent(); + } +} + +bool DummyEventLoop::hasEvents() { + return hasEvents_; +} + +void DummyEventLoop::eventPosted() { + hasEvents_ = true; +} } diff --git a/Swiften/EventLoop/DummyEventLoop.h b/Swiften/EventLoop/DummyEventLoop.h index 0e5e06d..da2a360 100644 --- a/Swiften/EventLoop/DummyEventLoop.h +++ b/Swiften/EventLoop/DummyEventLoop.h @@ -1,38 +1,29 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <deque> +#include <atomic> #include <Swiften/Base/API.h> #include <Swiften/EventLoop/EventLoop.h> namespace Swift { - class SWIFTEN_API DummyEventLoop : public EventLoop { - public: - DummyEventLoop(); - ~DummyEventLoop(); - - void processEvents() { - while (!events_.empty()) { - handleEvent(events_[0]); - events_.pop_front(); - } - } - - bool hasEvents() { - return !events_.empty(); - } - - virtual void post(const Event& event) { - events_.push_back(event); - } - - private: - std::deque<Event> events_; - }; + class SWIFTEN_API DummyEventLoop : public EventLoop { + public: + DummyEventLoop(); + virtual ~DummyEventLoop(); + + void processEvents(); + + bool hasEvents(); + + virtual void eventPosted(); + + private: + std::atomic<bool> hasEvents_ = ATOMIC_VAR_INIT(false); + }; } diff --git a/Swiften/EventLoop/Event.cpp b/Swiften/EventLoop/Event.cpp index aa002d3..15d7146 100644 --- a/Swiften/EventLoop/Event.cpp +++ b/Swiften/EventLoop/Event.cpp @@ -1,22 +1,22 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/EventLoop/Event.h> -#include <typeinfo> #include <iostream> +#include <typeinfo> std::ostream& operator<<(std::ostream& os, const Swift::Event& e) { - os << "Event(" << e.id << ","; - if (e.owner) { - os << typeid(*e.owner.get()).name(); - } - else { - os << "null"; - } - os << ")"; - return os; + os << "Event(" << e.id << ","; + if (e.owner) { + os << typeid(*e.owner.get()).name(); + } + else { + os << "null"; + } + os << ")"; + return os; } diff --git a/Swiften/EventLoop/Event.h b/Swiften/EventLoop/Event.h index b1d7cac..e92bb33 100644 --- a/Swiften/EventLoop/Event.h +++ b/Swiften/EventLoop/Event.h @@ -1,30 +1,31 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <boost/shared_ptr.hpp> +#include <memory> + #include <boost/function.hpp> #include <Swiften/EventLoop/EventOwner.h> namespace Swift { - class Event { - public: - Event(boost::shared_ptr<EventOwner> owner, const boost::function<void()>& callback) : id(~0U), owner(owner), callback(callback) { - } - - bool operator==(const Event& o) const { - return o.id == id; - } - - unsigned int id; - boost::shared_ptr<EventOwner> owner; - boost::function<void()> callback; - }; + class Event { + public: + Event(std::shared_ptr<EventOwner> owner, const boost::function<void()>& callback) : id(~0U), owner(owner), callback(callback) { + } + + bool operator==(const Event& o) const { + return o.id == id; + } + + unsigned int id; + std::shared_ptr<EventOwner> owner; + boost::function<void()> callback; + }; } std::ostream& operator<<(std::ostream& os, const Swift::Event& e); diff --git a/Swiften/EventLoop/EventLoop.cpp b/Swiften/EventLoop/EventLoop.cpp index 502bc49..1852f3f 100644 --- a/Swiften/EventLoop/EventLoop.cpp +++ b/Swiften/EventLoop/EventLoop.cpp @@ -1,36 +1,32 @@ /* - * Copyright (c) 2010-2013 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #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/thread/locks.hpp> +#include <vector> -#include <Swiften/Base/Log.h> +#include <boost/optional.hpp> -namespace lambda = boost::lambda; +#include <Swiften/Base/Log.h> namespace Swift { inline void invokeCallback(const Event& event) { - try { - assert(!event.callback.empty()); - event.callback(); - } - catch (const std::exception& e) { - std::cerr << "Uncaught exception in event loop: " << e.what() << std::endl; - } - catch (...) { - std::cerr << "Uncaught non-exception in event loop" << std::endl; - } + try { + assert(!event.callback.empty()); + event.callback(); + } + catch (const std::exception& e) { + SWIFT_LOG(error) << "Uncaught exception in event loop: " << e.what(); + } + catch (...) { + SWIFT_LOG(error) << "Uncaught non-exception in event loop"; + } } EventLoop::EventLoop() : nextEventID_(0), handlingEvents_(false) { @@ -39,55 +35,62 @@ EventLoop::EventLoop() : nextEventID_(0), handlingEvents_(false) { 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; - { - 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); - } - } - 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; - } +void EventLoop::handleNextEvent() { + // If handleNextEvent is already in progress, e.g. in case of a recursive call due to + // the event loop implementation, then do no handle further events. Instead call + // eventPosted() to continue event handling later. + bool callEventPosted = handlingEvents_; + if (!handlingEvents_) { + handlingEvents_ = true; + std::unique_lock<std::recursive_mutex> lock(removeEventsMutex_); + { + boost::optional<Event> nextEvent; + { + std::unique_lock<std::recursive_mutex> eventsLock(eventsMutex_); + if (!events_.empty()) { + nextEvent = events_.front(); + events_.pop_front(); + } + callEventPosted = !events_.empty(); + } + if (nextEvent) { + invokeCallback(*nextEvent); + } + } + handlingEvents_ = false; + } + + if (callEventPosted) { + eventPosted(); + } } -void EventLoop::postEvent(boost::function<void ()> callback, boost::shared_ptr<EventOwner> owner) { - Event event(owner, callback); - { - boost::lock_guard<boost::mutex> lock(eventsMutex_); - event.id = nextEventID_; - nextEventID_++; - events_.push_back(event); - } - //SWIFT_LOG(debug) << "Posting event " << event.id << std::endl; - post(event); +void EventLoop::postEvent(boost::function<void ()> callback, std::shared_ptr<EventOwner> owner) { + Event event(owner, callback); + bool callEventPosted = false; + { + std::unique_lock<std::recursive_mutex> lock(eventsMutex_); + + callEventPosted = events_.empty(); + + event.id = nextEventID_; + nextEventID_++; + events_.push_back(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); +void EventLoop::removeEventsFromOwner(std::shared_ptr<EventOwner> owner) { + std::unique_lock<std::recursive_mutex> removeLock(removeEventsMutex_, std::defer_lock); + std::unique_lock<std::recursive_mutex> eventsLock(eventsMutex_, std::defer_lock); + + std::lock(removeLock, eventsLock); + + events_.remove_if([&](const Event& event) { + return event.owner == owner; + }); } } diff --git a/Swiften/EventLoop/EventLoop.h b/Swiften/EventLoop/EventLoop.h index 587ba22..f61b9bc 100644 --- a/Swiften/EventLoop/EventLoop.h +++ b/Swiften/EventLoop/EventLoop.h @@ -1,44 +1,70 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <boost/function.hpp> -#include <boost/thread/mutex.hpp> #include <list> -#include <deque> +#include <mutex> + +#include <boost/function.hpp> #include <Swiften/Base/API.h> #include <Swiften/EventLoop/Event.h> namespace Swift { - class EventOwner; - - class SWIFTEN_API EventLoop { - public: - EventLoop(); - virtual ~EventLoop(); - - void postEvent(boost::function<void ()> event, boost::shared_ptr<EventOwner> owner = boost::shared_ptr<EventOwner>()); - void removeEventsFromOwner(boost::shared_ptr<EventOwner> owner); - - protected: - /** - * Reimplement this to call handleEvent(event) from the thread in which - * the event loop is residing. - */ - virtual void post(const Event& event) = 0; - - void handleEvent(const Event& event); - - private: - boost::mutex eventsMutex_; - unsigned int nextEventID_; - std::list<Event> events_; - bool handlingEvents_; - std::deque<Event> eventsToHandle_; - }; + 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 as \p owner, allowing later removal of events that have not yet been + * executed using the \ref removeEventsFromOwner method. + */ + void postEvent(boost::function<void ()> event, std::shared_ptr<EventOwner> owner = std::shared_ptr<EventOwner>()); + + /** + * The \ref removeEventsFromOwner method removes all events from the specified \p owner from the + * event queue. + */ + void removeEventsFromOwner(std::shared_ptr<EventOwner> owner); + + protected: + /** + * 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. + * If called recursively, the event queue is not further processed. Instead, \ref eventPosted + * is called to notify the implementing event loop of the non-empty event queue. + * It is recommended to not call \ref handleNextEvent inside an event posted to the event loop + * as this can lead to an infinite loop. + */ + void handleNextEvent(); + + /** + * 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: + unsigned int nextEventID_; + std::list<Event> events_; + bool handlingEvents_; + std::recursive_mutex eventsMutex_; + std::recursive_mutex removeEventsMutex_; + }; } diff --git a/Swiften/EventLoop/EventOwner.cpp b/Swiften/EventLoop/EventOwner.cpp index 9970499..56e0faa 100644 --- a/Swiften/EventLoop/EventOwner.cpp +++ b/Swiften/EventLoop/EventOwner.cpp @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #include <Swiften/EventLoop/EventOwner.h> diff --git a/Swiften/EventLoop/EventOwner.h b/Swiften/EventLoop/EventOwner.h index 43a059b..cd4a80b 100644 --- a/Swiften/EventLoop/EventOwner.h +++ b/Swiften/EventLoop/EventOwner.h @@ -1,7 +1,7 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once @@ -9,8 +9,8 @@ #include <Swiften/Base/API.h> namespace Swift { - class SWIFTEN_API EventOwner { - public: - virtual ~EventOwner(); - }; + class SWIFTEN_API EventOwner { + public: + virtual ~EventOwner(); + }; } diff --git a/Swiften/EventLoop/Qt/QtEventLoop.h b/Swiften/EventLoop/Qt/QtEventLoop.h index 0097cf9..cf374ab 100644 --- a/Swiften/EventLoop/Qt/QtEventLoop.h +++ b/Swiften/EventLoop/Qt/QtEventLoop.h @@ -1,47 +1,59 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <QObject> -#include <QEvent> +#include <mutex> + #include <QCoreApplication> +#include <QEvent> +#include <QObject> #include <Swiften/EventLoop/EventLoop.h> namespace Swift { - class QtEventLoop : public QObject, public EventLoop { - public: - QtEventLoop() {} - ~QtEventLoop() { - QCoreApplication::removePostedEvents(this); - } - - virtual void post(const Swift::Event& event) { - QCoreApplication::postEvent(this, new Event(event)); - } - - virtual bool event(QEvent* qevent) { - Event* event = dynamic_cast<Event*>(qevent); - if (event) { - handleEvent(event->event_); - //event->deleteLater(); FIXME: Leak? - return true; - } - - return false; - } - - private: - struct Event : public QEvent { - Event(const Swift::Event& event) : - QEvent(QEvent::User), event_(event) { - } - - Swift::Event event_; - }; - }; + class QtEventLoop : public QObject, public EventLoop { + public: + QtEventLoop() : isEventInQtEventLoop_(false) {} + virtual ~QtEventLoop() { + QCoreApplication::removePostedEvents(this); + } + + protected: + virtual void eventPosted() { + std::unique_lock<std::recursive_mutex> lock(isEventInQtEventLoopMutex_); + if (!isEventInQtEventLoop_) { + isEventInQtEventLoop_ = true; + QCoreApplication::postEvent(this, new Event()); + } + } + + virtual bool event(QEvent* qevent) { + Event* event = dynamic_cast<Event*>(qevent); + if (event) { + { + std::unique_lock<std::recursive_mutex> lock(isEventInQtEventLoopMutex_); + isEventInQtEventLoop_ = false; + } + handleNextEvent(); + //event->deleteLater(); FIXME: Leak? + return true; + } + + return false; + } + + private: + struct Event : public QEvent { + Event() : + QEvent(QEvent::User) { + } + }; + + bool isEventInQtEventLoop_; + std::recursive_mutex isEventInQtEventLoopMutex_; + }; } diff --git a/Swiften/EventLoop/SConscript b/Swiften/EventLoop/SConscript index 8bef8fb..7aea53f 100644 --- a/Swiften/EventLoop/SConscript +++ b/Swiften/EventLoop/SConscript @@ -1,22 +1,23 @@ Import("swiften_env") sources = [ - "EventLoop.cpp", - "EventOwner.cpp", - "Event.cpp", - "SimpleEventLoop.cpp", - "DummyEventLoop.cpp", - "SingleThreadedEventLoop.cpp", - ] + "BoostASIOEventLoop.cpp", + "DummyEventLoop.cpp", + "Event.cpp", + "EventLoop.cpp", + "EventOwner.cpp", + "SimpleEventLoop.cpp", + "SingleThreadedEventLoop.cpp", + ] objects = swiften_env.SwiftenObject(sources) swiften_env.Append(SWIFTEN_OBJECTS = [objects]) -if swiften_env["PLATFORM"] == "darwin" and swiften_env["target"] == "native": - myenv = swiften_env.Clone() - myenv.Append(CXXFLAGS = myenv["OBJCCFLAGS"]) - objects = myenv.SwiftenObject([ - "Cocoa/CocoaEventLoop.mm", - "Cocoa/CocoaEvent.mm" - ]) - swiften_env.Append(SWIFTEN_OBJECTS = [objects]) +if swiften_env["PLATFORM"] == "darwin" and swiften_env["target"] == "native" or swiften_env["target"] == 'xcode': + myenv = swiften_env.Clone() + myenv.Append(CXXFLAGS = myenv["OBJCCFLAGS"]) + objects = myenv.SwiftenObject([ + "Cocoa/CocoaEventLoop.mm", + "Cocoa/CocoaEvent.mm" + ]) + swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/EventLoop/SimpleEventLoop.cpp b/Swiften/EventLoop/SimpleEventLoop.cpp index 42a5481..745fadb 100644 --- a/Swiften/EventLoop/SimpleEventLoop.cpp +++ b/Swiften/EventLoop/SimpleEventLoop.cpp @@ -1,72 +1,56 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #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); - } - events.swap(events_); - } - foreach(const Event& event, events) { - handleEvent(event); - } - if (breakAfterEvents) { - return; - } - } + while (isRunning_) { + { + std::unique_lock<std::mutex> lock(eventAvailableMutex_); + while (!eventAvailable_) { + eventAvailableCondition_.wait(lock); + } + + eventAvailable_ = false; + } + runOnce(); + if (breakAfterEvents) { + return; + } + } } 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() { - postEvent(boost::bind(&SimpleEventLoop::doStop, this)); + postEvent(boost::bind(&SimpleEventLoop::doStop, this)); } void SimpleEventLoop::doStop() { - isRunning_ = false; + isRunning_ = false; } -void SimpleEventLoop::post(const Event& event) { - { - boost::lock_guard<boost::mutex> lock(eventsMutex_); - events_.push_back(event); - } - eventsAvailable_.notify_one(); +void SimpleEventLoop::eventPosted() { + { + std::unique_lock<std::mutex> lock(eventAvailableMutex_); + eventAvailable_ = true; + } + eventAvailableCondition_.notify_one(); } diff --git a/Swiften/EventLoop/SimpleEventLoop.h b/Swiften/EventLoop/SimpleEventLoop.h index da1c039..fe5f509 100644 --- a/Swiften/EventLoop/SimpleEventLoop.h +++ b/Swiften/EventLoop/SimpleEventLoop.h @@ -1,47 +1,47 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ #pragma once -#include <vector> -#include <boost/function.hpp> -#include <boost/thread/mutex.hpp> -#include <boost/thread/condition_variable.hpp> +#include <condition_variable> +#include <mutex> #include <Swiften/Base/API.h> #include <Swiften/EventLoop/EventLoop.h> namespace Swift { - class SWIFTEN_API SimpleEventLoop : public EventLoop { - public: - SimpleEventLoop(); - ~SimpleEventLoop(); + class SWIFTEN_API SimpleEventLoop : public EventLoop { + public: + SimpleEventLoop(); + virtual ~SimpleEventLoop(); - void run() { - doRun(false); - } + void run() { + doRun(false); + } - void runUntilEvents() { - doRun(true); - } + void runUntilEvents() { + doRun(true); + } - void runOnce(); + void runOnce(); - void stop(); + void stop(); - virtual void post(const Event& event); + protected: + virtual void eventPosted(); - private: - void doRun(bool breakAfterEvents); - void doStop(); + private: + void doRun(bool breakAfterEvents); + void doStop(); - private: - bool isRunning_; - std::vector<Event> events_; - boost::mutex eventsMutex_; - boost::condition_variable eventsAvailable_; - }; + private: + bool isRunning_; + + bool eventAvailable_; + std::mutex eventAvailableMutex_; + std::condition_variable eventAvailableCondition_; + }; } diff --git a/Swiften/EventLoop/SingleThreadedEventLoop.cpp b/Swiften/EventLoop/SingleThreadedEventLoop.cpp index c2235b1..89b4460 100644 --- a/Swiften/EventLoop/SingleThreadedEventLoop.cpp +++ b/Swiften/EventLoop/SingleThreadedEventLoop.cpp @@ -4,62 +4,58 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ -#include "Swiften/EventLoop/SingleThreadedEventLoop.h" +/* + * Copyright (c) 2016-2019 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ -#include <boost/bind.hpp> -#include <iostream> +#include <Swiften/EventLoop/SingleThreadedEventLoop.h> -#include "Swiften/Base/foreach.h" +#include <iostream> +#include <boost/bind.hpp> namespace Swift { -SingleThreadedEventLoop::SingleThreadedEventLoop() -: shouldShutDown_(false) +SingleThreadedEventLoop::SingleThreadedEventLoop() +: 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); - } - - if (shouldShutDown_) - throw EventLoopCanceledException(); + std::unique_lock<std::mutex> lock(eventAvailableMutex_); + while (!eventAvailable_ && !shouldShutDown_) { + eventAvailableCondition_.wait(lock); + } + + if (shouldShutDown_) { + throw EventLoopCanceledException(); + } } 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); - } + { + std::lock_guard<std::mutex> lock(eventAvailableMutex_); + eventAvailable_ = false; + } + handleNextEvent(); } void SingleThreadedEventLoop::stop() { - boost::unique_lock<boost::mutex> lock(eventsMutex_); - shouldShutDown_ = true; - eventsAvailable_.notify_one(); + std::unique_lock<std::mutex> lock(eventAvailableMutex_); + shouldShutDown_ = true; + 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() { + std::lock_guard<std::mutex> lock(eventAvailableMutex_); + eventAvailable_ = true; + eventAvailableCondition_.notify_one(); } } // namespace Swift diff --git a/Swiften/EventLoop/SingleThreadedEventLoop.h b/Swiften/EventLoop/SingleThreadedEventLoop.h index 75ffad0..9f8cb0a 100644 --- a/Swiften/EventLoop/SingleThreadedEventLoop.h +++ b/Swiften/EventLoop/SingleThreadedEventLoop.h @@ -4,13 +4,19 @@ * See Documentation/Licenses/BSD-simplified.txt for more information. */ +/* + * Copyright (c) 2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. + */ + #pragma once +#include <condition_variable> +#include <mutex> #include <vector> -#include <boost/thread/mutex.hpp> -#include <boost/thread/condition_variable.hpp> -#include "Swiften/EventLoop/EventLoop.h" +#include <Swiften/EventLoop/EventLoop.h> // DESCRIPTION: // @@ -22,37 +28,39 @@ // The SingleThreadedEventLoop class implements an event loop that can be used from such applications. // // USAGE: -// -// Spawn a new thread in the desired framework and call SingleThreadedEventLoop::waitForEvents(). The method +// +// Spawn a new thread in the desired framework and call SingleThreadedEventLoop::waitForEvents(). The method // blocks until a new event has arrived at which time it'll return, or until the wait is canceled -// at which time it throws EventLoopCanceledException. +// at which time it throws EventLoopCanceledException. // // When a new event has arrived and SingleThreadedEventLoop::waitForEvents() returns, the caller should then -// call SingleThreadedEventLoop::handleEvents() on the main GUI thread. For WPF applications, for instance, +// call SingleThreadedEventLoop::handleEvents() on the main GUI thread. For WPF applications, for instance, // the Dispatcher class can be used to execute the call on the GUI thread. // namespace Swift { - class SingleThreadedEventLoop : public EventLoop { - public: - class EventLoopCanceledException : public std::exception { }; - - public: - SingleThreadedEventLoop(); - ~SingleThreadedEventLoop(); - - // Blocks while waiting for new events and returns when new events are available. - // Throws EventLoopCanceledException when the wait is canceled. - void waitForEvents(); - void handleEvents(); - void stop(); - - virtual void post(const Event& event); - - private: - bool shouldShutDown_; - std::vector<Event> events_; - boost::mutex eventsMutex_; - boost::condition_variable eventsAvailable_; - }; + class SingleThreadedEventLoop : public EventLoop { + public: + class EventLoopCanceledException : public std::exception { }; + + public: + SingleThreadedEventLoop(); + virtual ~SingleThreadedEventLoop(); + + // Blocks while waiting for new events and returns when new events are available. + // Throws EventLoopCanceledException when the wait is canceled. + void waitForEvents(); + void handleEvents(); + void stop(); + + protected: + virtual void eventPosted(); + + private: + bool shouldShutDown_; + + bool eventAvailable_; + std::mutex eventAvailableMutex_; + std::condition_variable eventAvailableCondition_; + }; } diff --git a/Swiften/EventLoop/UnitTest/EventLoopTest.cpp b/Swiften/EventLoop/UnitTest/EventLoopTest.cpp index 58396e6..26c56d3 100644 --- a/Swiften/EventLoop/UnitTest/EventLoopTest.cpp +++ b/Swiften/EventLoop/UnitTest/EventLoopTest.cpp @@ -1,90 +1,105 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ +#include <thread> + +#include <boost/bind.hpp> + #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <boost/thread.hpp> -#include <boost/bind.hpp> +#include <Swiften/Base/sleep.h> +#include <Swiften/EventLoop/DummyEventLoop.h> #include <Swiften/EventLoop/EventOwner.h> #include <Swiften/EventLoop/SimpleEventLoop.h> -#include <Swiften/EventLoop/DummyEventLoop.h> -#include <Swiften/Base/sleep.h> using namespace Swift; class EventLoopTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(EventLoopTest); - CPPUNIT_TEST(testPost); - CPPUNIT_TEST(testRemove); - CPPUNIT_TEST(testHandleEvent_Recursive); - CPPUNIT_TEST_SUITE_END(); - - public: - void setUp() { - events_.clear(); - } - - void testPost() { - SimpleEventLoop testling; - - testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 1)); - testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 2)); - testling.stop(); - testling.run(); - - CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(events_.size())); - CPPUNIT_ASSERT_EQUAL(1, events_[0]); - CPPUNIT_ASSERT_EQUAL(2, events_[1]); - } - - void testRemove() { - SimpleEventLoop testling; - boost::shared_ptr<MyEventOwner> eventOwner1(new MyEventOwner()); - boost::shared_ptr<MyEventOwner> eventOwner2(new MyEventOwner()); - - testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 1), eventOwner1); - testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 2), eventOwner2); - testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 3), eventOwner1); - testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 4), eventOwner2); - testling.removeEventsFromOwner(eventOwner2); - testling.stop(); - testling.run(); - - CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(events_.size())); - CPPUNIT_ASSERT_EQUAL(1, events_[0]); - CPPUNIT_ASSERT_EQUAL(3, events_[1]); - } - - void testHandleEvent_Recursive() { - DummyEventLoop testling; - boost::shared_ptr<MyEventOwner> eventOwner(new MyEventOwner()); - - testling.postEvent(boost::bind(&EventLoopTest::runEventLoop, this, &testling, eventOwner), eventOwner); - testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 0), eventOwner); - testling.processEvents(); - - CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(events_.size())); - CPPUNIT_ASSERT_EQUAL(0, events_[0]); - CPPUNIT_ASSERT_EQUAL(1, events_[1]); - } - - private: - struct MyEventOwner : public EventOwner {}; - void logEvent(int i) { - events_.push_back(i); - } - void runEventLoop(DummyEventLoop* loop, boost::shared_ptr<MyEventOwner> eventOwner) { - loop->processEvents(); - CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(events_.size())); - loop->postEvent(boost::bind(&EventLoopTest::logEvent, this, 1), eventOwner); - } - - private: - std::vector<int> events_; + CPPUNIT_TEST_SUITE(EventLoopTest); + CPPUNIT_TEST(testPost); + CPPUNIT_TEST(testRemove); + CPPUNIT_TEST(testHandleEvent_Recursive); + CPPUNIT_TEST(testHandleEvent_FirstEventRemovesSecondEvent); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + events_.clear(); + } + + void testPost() { + SimpleEventLoop testling; + + testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 1)); + testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 2)); + testling.stop(); + testling.run(); + + CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(events_.size())); + CPPUNIT_ASSERT_EQUAL(1, events_[0]); + CPPUNIT_ASSERT_EQUAL(2, events_[1]); + } + + void testRemove() { + SimpleEventLoop testling; + std::shared_ptr<MyEventOwner> eventOwner1(new MyEventOwner()); + std::shared_ptr<MyEventOwner> eventOwner2(new MyEventOwner()); + + testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 1), eventOwner1); + testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 2), eventOwner2); + testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 3), eventOwner1); + testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 4), eventOwner2); + testling.removeEventsFromOwner(eventOwner2); + testling.stop(); + testling.run(); + + CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(events_.size())); + CPPUNIT_ASSERT_EQUAL(1, events_[0]); + CPPUNIT_ASSERT_EQUAL(3, events_[1]); + } + + void testHandleEvent_Recursive() { + DummyEventLoop testling; + std::shared_ptr<MyEventOwner> eventOwner(new MyEventOwner()); + + testling.postEvent(boost::bind(&EventLoopTest::runEventLoop, this, &testling, eventOwner), eventOwner); + testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 0), eventOwner); + testling.processEvents(); + + CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(events_.size())); + CPPUNIT_ASSERT_EQUAL(0, events_[0]); + CPPUNIT_ASSERT_EQUAL(1, events_[1]); + } + + void testHandleEvent_FirstEventRemovesSecondEvent() { + DummyEventLoop testling; + auto eventOwner = std::make_shared<MyEventOwner>(); + auto secondEventFired = false; + + testling.postEvent([&](){ testling.removeEventsFromOwner(eventOwner); }, eventOwner); + testling.postEvent([&](){ secondEventFired = true; }, eventOwner); + testling.processEvents(); + + CPPUNIT_ASSERT_EQUAL(false, secondEventFired); + } + + private: + struct MyEventOwner : public EventOwner {}; + void logEvent(int i) { + events_.push_back(i); + } + void runEventLoop(DummyEventLoop* loop, std::shared_ptr<MyEventOwner> eventOwner) { + loop->processEvents(); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(events_.size())); + loop->postEvent(boost::bind(&EventLoopTest::logEvent, this, 1), eventOwner); + } + + private: + std::vector<int> events_; }; CPPUNIT_TEST_SUITE_REGISTRATION(EventLoopTest); diff --git a/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp b/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp index 475b6b5..3d096d3 100644 --- a/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp +++ b/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp @@ -1,67 +1,69 @@ /* - * Copyright (c) 2010 Remko Tronçon - * Licensed under the GNU General Public License v3. - * See Documentation/Licenses/GPLv3.txt for more information. + * Copyright (c) 2010-2016 Isode Limited. + * All rights reserved. + * See the COPYING file for more information. */ +#include <thread> + +#include <boost/bind.hpp> + #include <cppunit/extensions/HelperMacros.h> #include <cppunit/extensions/TestFactoryRegistry.h> -#include <boost/thread.hpp> -#include <boost/bind.hpp> -#include <Swiften/EventLoop/SimpleEventLoop.h> #include <Swiften/Base/sleep.h> +#include <Swiften/EventLoop/SimpleEventLoop.h> using namespace Swift; class SimpleEventLoopTest : public CppUnit::TestFixture { - CPPUNIT_TEST_SUITE(SimpleEventLoopTest); - // FIXME: Temporarily disabling run, because it generates a "vector - // iterator not incrementable" on XP - //CPPUNIT_TEST(testRun); - CPPUNIT_TEST(testPostFromMainThread); - CPPUNIT_TEST_SUITE_END(); - - public: - void setUp() { - counter_ = 0; - } - - void testRun() { - SimpleEventLoop testling; - boost::thread thread(boost::bind(&SimpleEventLoopTest::runIncrementingThread, this, &testling)); - testling.run(); - - CPPUNIT_ASSERT_EQUAL(10, counter_); - } - - void testPostFromMainThread() { - SimpleEventLoop testling; - testling.postEvent(boost::bind(&SimpleEventLoopTest::incrementCounterAndStop, this, &testling)); - testling.run(); - - CPPUNIT_ASSERT_EQUAL(1, counter_); - } - - private: - void runIncrementingThread(SimpleEventLoop* loop) { - for (unsigned int i = 0; i < 10; ++i) { - Swift::sleep(1); - loop->postEvent(boost::bind(&SimpleEventLoopTest::incrementCounter, this)); - } - loop->stop(); - } - - void incrementCounter() { - counter_++; - } - - void incrementCounterAndStop(SimpleEventLoop* loop) { - counter_++; - loop->stop(); - } - - int counter_; + CPPUNIT_TEST_SUITE(SimpleEventLoopTest); + // FIXME: Temporarily disabling run, because it generates a "vector + // iterator not incrementable" on XP + //CPPUNIT_TEST(testRun); + CPPUNIT_TEST(testPostFromMainThread); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp() { + counter_ = 0; + } + + void testRun() { + SimpleEventLoop testling; + std::thread thread(boost::bind(&SimpleEventLoopTest::runIncrementingThread, this, &testling)); + testling.run(); + + CPPUNIT_ASSERT_EQUAL(10, counter_); + } + + void testPostFromMainThread() { + SimpleEventLoop testling; + testling.postEvent(boost::bind(&SimpleEventLoopTest::incrementCounterAndStop, this, &testling)); + testling.run(); + + CPPUNIT_ASSERT_EQUAL(1, counter_); + } + + private: + void runIncrementingThread(SimpleEventLoop* loop) { + for (unsigned int i = 0; i < 10; ++i) { + Swift::sleep(1); + loop->postEvent(boost::bind(&SimpleEventLoopTest::incrementCounter, this)); + } + loop->stop(); + } + + void incrementCounter() { + counter_++; + } + + void incrementCounterAndStop(SimpleEventLoop* loop) { + counter_++; + loop->stop(); + } + + int counter_; }; CPPUNIT_TEST_SUITE_REGISTRATION(SimpleEventLoopTest); |