diff options
Diffstat (limited to 'Swiften/EventLoop')
-rw-r--r-- | Swiften/EventLoop/Cocoa/CocoaEvent.h | 20 | ||||
-rw-r--r-- | Swiften/EventLoop/Cocoa/CocoaEvent.mm | 25 | ||||
-rw-r--r-- | Swiften/EventLoop/Cocoa/CocoaEventLoop.h | 14 | ||||
-rw-r--r-- | Swiften/EventLoop/Cocoa/CocoaEventLoop.mm | 21 | ||||
-rw-r--r-- | Swiften/EventLoop/Deleter.h | 23 | ||||
-rw-r--r-- | Swiften/EventLoop/DummyEventLoop.h | 41 | ||||
-rw-r--r-- | Swiften/EventLoop/Event.h | 22 | ||||
-rw-r--r-- | Swiften/EventLoop/EventLoop.cpp | 49 | ||||
-rw-r--r-- | Swiften/EventLoop/EventLoop.h | 38 | ||||
-rw-r--r-- | Swiften/EventLoop/EventOwner.cpp | 8 | ||||
-rw-r--r-- | Swiften/EventLoop/EventOwner.h | 8 | ||||
-rw-r--r-- | Swiften/EventLoop/MainEventLoop.cpp | 36 | ||||
-rw-r--r-- | Swiften/EventLoop/MainEventLoop.h | 41 | ||||
-rw-r--r-- | Swiften/EventLoop/Qt/QtEventLoop.h | 36 | ||||
-rw-r--r-- | Swiften/EventLoop/SConscript | 17 | ||||
-rw-r--r-- | Swiften/EventLoop/SimpleEventLoop.cpp | 54 | ||||
-rw-r--r-- | Swiften/EventLoop/SimpleEventLoop.h | 30 | ||||
-rw-r--r-- | Swiften/EventLoop/UnitTest/EventLoopTest.cpp | 67 | ||||
-rw-r--r-- | Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp | 62 |
19 files changed, 612 insertions, 0 deletions
diff --git a/Swiften/EventLoop/Cocoa/CocoaEvent.h b/Swiften/EventLoop/Cocoa/CocoaEvent.h new file mode 100644 index 0000000..0ff4453 --- /dev/null +++ b/Swiften/EventLoop/Cocoa/CocoaEvent.h @@ -0,0 +1,20 @@ +#pragma once + +#include <Cocoa/Cocoa.h> + +namespace Swift { + class Event; + class CocoaEventLoop; +} + +@interface CocoaEvent : NSObject { + Swift::Event* event; + Swift::CocoaEventLoop* eventLoop; +} + +// Takes ownership of event +- (id) initWithEvent: (Swift::Event*) e eventLoop: (Swift::CocoaEventLoop*) el; +- (void) process; +- (void) dealloc; + +@end diff --git a/Swiften/EventLoop/Cocoa/CocoaEvent.mm b/Swiften/EventLoop/Cocoa/CocoaEvent.mm new file mode 100644 index 0000000..8a90983 --- /dev/null +++ b/Swiften/EventLoop/Cocoa/CocoaEvent.mm @@ -0,0 +1,25 @@ +#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; +} + +- (void) process { + eventLoop->handleEvent(*event); +} + +- (void) dealloc { + delete event; + [super dealloc]; +} + +@end diff --git a/Swiften/EventLoop/Cocoa/CocoaEventLoop.h b/Swiften/EventLoop/Cocoa/CocoaEventLoop.h new file mode 100644 index 0000000..ad8e456 --- /dev/null +++ b/Swiften/EventLoop/Cocoa/CocoaEventLoop.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Swiften/EventLoop/EventLoop.h" + +namespace Swift { + class CocoaEventLoop : public EventLoop { + public: + CocoaEventLoop(); + + virtual void post(const Event& event); + + using EventLoop::handleEvent; + }; +} diff --git a/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm b/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm new file mode 100644 index 0000000..b90f3c6 --- /dev/null +++ b/Swiften/EventLoop/Cocoa/CocoaEventLoop.mm @@ -0,0 +1,21 @@ +#include "Swiften/EventLoop/Cocoa/CocoaEventLoop.h" +#include "Swiften/EventLoop/Cocoa/CocoaEvent.h" + +#pragma GCC diagnostic ignored "-Wold-style-cast" + +namespace Swift { + +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]; +} + +} diff --git a/Swiften/EventLoop/Deleter.h b/Swiften/EventLoop/Deleter.h new file mode 100644 index 0000000..217a17f --- /dev/null +++ b/Swiften/EventLoop/Deleter.h @@ -0,0 +1,23 @@ +#ifndef SWIFTEN_Deleter_H +#define SWIFTEN_Deleter_H + +#include <cassert> + +namespace Swift { + template<typename T> + class Deleter { + public: + Deleter(T* object) : object_(object) { + } + + void operator()() { + assert(object_); + delete object_; + object_ = 0; + } + + private: + T* object_; + }; +} +#endif diff --git a/Swiften/EventLoop/DummyEventLoop.h b/Swiften/EventLoop/DummyEventLoop.h new file mode 100644 index 0000000..7766bd4 --- /dev/null +++ b/Swiften/EventLoop/DummyEventLoop.h @@ -0,0 +1,41 @@ +#pragma once + +#include <deque> +#include <iostream> +#include <boost/function.hpp> + +#include "Swiften/EventLoop/EventLoop.h" +#include "Swiften/Base/foreach.h" + +namespace Swift { + class DummyEventLoop : public EventLoop { + public: + DummyEventLoop() { + } + + ~DummyEventLoop() { + if (!events_.empty()) { + std::cerr << "DummyEventLoop: Unhandled events at destruction time" << std::endl; + } + events_.clear(); + } + + void processEvents() { + while (!events_.empty()) { + handleEvent(events_[0]); + events_.pop_front(); + } + } + + bool hasEvents() { + return events_.size() > 0; + } + + virtual void post(const Event& event) { + events_.push_back(event); + } + + private: + std::deque<Event> events_; + }; +} diff --git a/Swiften/EventLoop/Event.h b/Swiften/EventLoop/Event.h new file mode 100644 index 0000000..edd35f4 --- /dev/null +++ b/Swiften/EventLoop/Event.h @@ -0,0 +1,22 @@ +#pragma once + +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> + +#include "Swiften/EventLoop/EventOwner.h" + +namespace Swift { + struct Event { + Event(boost::shared_ptr<EventOwner> owner, const boost::function<void()>& callback) : + 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; + }; +} diff --git a/Swiften/EventLoop/EventLoop.cpp b/Swiften/EventLoop/EventLoop.cpp new file mode 100644 index 0000000..3c3c356 --- /dev/null +++ b/Swiften/EventLoop/EventLoop.cpp @@ -0,0 +1,49 @@ +#include "Swiften/EventLoop/EventLoop.h" + +#include <algorithm> +#include <boost/bind.hpp> + +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + +EventLoop::EventLoop() : nextEventID_(0) { + MainEventLoop::setInstance(this); +} + +EventLoop::~EventLoop() { + MainEventLoop::resetInstance(); +} + +void EventLoop::handleEvent(const Event& event) { + 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) { + event.callback(); + } +} + +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); + } + post(event); +} + +void EventLoop::removeEventsFromOwner(boost::shared_ptr<EventOwner> owner) { + boost::lock_guard<boost::mutex> lock(eventsMutex_); + events_.remove_if(HasOwner(owner)); +} + +} diff --git a/Swiften/EventLoop/EventLoop.h b/Swiften/EventLoop/EventLoop.h new file mode 100644 index 0000000..2b45288 --- /dev/null +++ b/Swiften/EventLoop/EventLoop.h @@ -0,0 +1,38 @@ +#pragma once + +#include <boost/function.hpp> +#include <boost/thread/mutex.hpp> +#include <list> + +#include "Swiften/EventLoop/Event.h" + +namespace Swift { + class EventOwner; + class 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: + struct HasOwner { + HasOwner(boost::shared_ptr<EventOwner> owner) : owner(owner) {} + bool operator()(const Event& event) { return event.owner == owner; } + boost::shared_ptr<EventOwner> owner; + }; + boost::mutex eventsMutex_; + unsigned int nextEventID_; + std::list<Event> events_; + }; +} diff --git a/Swiften/EventLoop/EventOwner.cpp b/Swiften/EventLoop/EventOwner.cpp new file mode 100644 index 0000000..4818b3c --- /dev/null +++ b/Swiften/EventLoop/EventOwner.cpp @@ -0,0 +1,8 @@ +#include "Swiften/EventLoop/EventOwner.h" + +namespace Swift { + +EventOwner::~EventOwner() { +} + +} diff --git a/Swiften/EventLoop/EventOwner.h b/Swiften/EventLoop/EventOwner.h new file mode 100644 index 0000000..8da95e0 --- /dev/null +++ b/Swiften/EventLoop/EventOwner.h @@ -0,0 +1,8 @@ +#pragma once + +namespace Swift { + class EventOwner { + public: + virtual ~EventOwner(); + }; +} diff --git a/Swiften/EventLoop/MainEventLoop.cpp b/Swiften/EventLoop/MainEventLoop.cpp new file mode 100644 index 0000000..d7de6c6 --- /dev/null +++ b/Swiften/EventLoop/MainEventLoop.cpp @@ -0,0 +1,36 @@ +#include "Swiften/EventLoop/MainEventLoop.h" + +#include <iostream> +#include <typeinfo> + +namespace Swift { + +EventLoop* MainEventLoop::getInstance() { + if (!instance_) { + std::cerr << "No main event loop instantiated. Please instantiate the appropriate subclass of EventLoop (e.g. SimpleEventLoop, QtEventLoop) at the start of your application." << std::endl; + exit(-1); + } + return instance_; +} + +void MainEventLoop::setInstance(EventLoop* loop) { + assert(!instance_); + instance_ = loop; +} + +void MainEventLoop::resetInstance() { + assert(instance_); + instance_ = 0; +} + +void MainEventLoop::postEvent(boost::function<void ()> event, boost::shared_ptr<EventOwner> owner) { + getInstance()->postEvent(event, owner); +} + +void MainEventLoop::removeEventsFromOwner(boost::shared_ptr<EventOwner> owner) { + getInstance()->removeEventsFromOwner(owner); +} + +EventLoop* MainEventLoop::instance_ = 0; + +} diff --git a/Swiften/EventLoop/MainEventLoop.h b/Swiften/EventLoop/MainEventLoop.h new file mode 100644 index 0000000..fbb7079 --- /dev/null +++ b/Swiften/EventLoop/MainEventLoop.h @@ -0,0 +1,41 @@ +#ifndef SWIFTEN_MainEventLoop_H +#define SWIFTEN_MainEventLoop_H + +#include <boost/function.hpp> + +#include "Swiften/EventLoop/Deleter.h" +#include "Swiften/EventLoop/EventLoop.h" + +namespace Swift { + class EventLoop; + class EventOwner; + + class MainEventLoop { + friend class EventLoop; + + public: + /** + * Post an event from the given owner to the event loop. + * If the owner is destroyed, all events should be removed from the + * loop using removeEventsFromOwner(). + */ + static void postEvent(boost::function<void ()> event, boost::shared_ptr<EventOwner> owner = boost::shared_ptr<EventOwner>()); + + static void removeEventsFromOwner(boost::shared_ptr<EventOwner> owner); + + template<typename T> + static void deleteLater(T* t) { + getInstance()->postEvent(Deleter<T>(t), 0); + } + + private: + static void setInstance(EventLoop*); + static void resetInstance(); + static EventLoop* getInstance(); + + private: + static EventLoop* instance_; + }; +} + +#endif diff --git a/Swiften/EventLoop/Qt/QtEventLoop.h b/Swiften/EventLoop/Qt/QtEventLoop.h new file mode 100644 index 0000000..40e927e --- /dev/null +++ b/Swiften/EventLoop/Qt/QtEventLoop.h @@ -0,0 +1,36 @@ +#pragma once + +#include <QObject> +#include <QEvent> +#include <QCoreApplication> + +#include "Swiften/EventLoop/EventLoop.h" + +class QtEventLoop : public QObject, public Swift::EventLoop { + public: + QtEventLoop() {} + + 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_; + }; +}; diff --git a/Swiften/EventLoop/SConscript b/Swiften/EventLoop/SConscript new file mode 100644 index 0000000..5d1c3cb --- /dev/null +++ b/Swiften/EventLoop/SConscript @@ -0,0 +1,17 @@ +Import("swiften_env") + +sources = [ + "EventLoop.cpp", + "EventOwner.cpp", + "MainEventLoop.cpp", + "SimpleEventLoop.cpp", + ] + +if swiften_env["PLATFORM"] == "darwin" and swiften_env["target"] == "native": + sources += [ + "Cocoa/CocoaEventLoop.mm", + "Cocoa/CocoaEvent.mm" + ] + +objects = swiften_env.StaticObject(sources) +swiften_env.Append(SWIFTEN_OBJECTS = [objects]) diff --git a/Swiften/EventLoop/SimpleEventLoop.cpp b/Swiften/EventLoop/SimpleEventLoop.cpp new file mode 100644 index 0000000..7c46ed3 --- /dev/null +++ b/Swiften/EventLoop/SimpleEventLoop.cpp @@ -0,0 +1,54 @@ +#include "Swiften/EventLoop/SimpleEventLoop.h" + +#include <boost/bind.hpp> + +#include "Swiften/Base/foreach.h" + + +namespace Swift { + +void nop() {} + +SimpleEventLoop::SimpleEventLoop() : isRunning_(true) { +} + +SimpleEventLoop::~SimpleEventLoop() { + if (!events_.empty()) { + std::cerr << "Warning: Pending events in SimpleEventLoop at destruction time" << std::endl; + } +} + +void SimpleEventLoop::run() { + while (isRunning_) { + std::vector<Event> events; + { + boost::unique_lock<boost::mutex> lock(eventsMutex_); + while (events_.size() == 0) { + eventsAvailable_.wait(lock); + } + events.swap(events_); + } + foreach(const Event& event, events) { + handleEvent(event); + } + } +} + +void SimpleEventLoop::stop() { + postEvent(boost::bind(&SimpleEventLoop::doStop, this)); +} + +void SimpleEventLoop::doStop() { + isRunning_ = false; +} + +void SimpleEventLoop::post(const Event& event) { + { + boost::lock_guard<boost::mutex> lock(eventsMutex_); + events_.push_back(event); + } + eventsAvailable_.notify_one(); +} + + +} diff --git a/Swiften/EventLoop/SimpleEventLoop.h b/Swiften/EventLoop/SimpleEventLoop.h new file mode 100644 index 0000000..bd0a07f --- /dev/null +++ b/Swiften/EventLoop/SimpleEventLoop.h @@ -0,0 +1,30 @@ +#pragma once + +#include <vector> +#include <boost/function.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition_variable.hpp> + +#include "Swiften/EventLoop/EventLoop.h" + +namespace Swift { + class SimpleEventLoop : public EventLoop { + public: + SimpleEventLoop(); + ~SimpleEventLoop(); + + void run(); + void stop(); + + virtual void post(const Event& event); + + private: + void doStop(); + + private: + bool isRunning_; + std::vector<Event> events_; + boost::mutex eventsMutex_; + boost::condition_variable eventsAvailable_; + }; +} diff --git a/Swiften/EventLoop/UnitTest/EventLoopTest.cpp b/Swiften/EventLoop/UnitTest/EventLoopTest.cpp new file mode 100644 index 0000000..9475ac9 --- /dev/null +++ b/Swiften/EventLoop/UnitTest/EventLoopTest.cpp @@ -0,0 +1,67 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <boost/thread.hpp> +#include <boost/bind.hpp> + +#include "Swiften/EventLoop/EventOwner.h" +#include "Swiften/EventLoop/SimpleEventLoop.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_SUITE_END(); + + public: + EventLoopTest() {} + + 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]); + } + + private: + struct MyEventOwner : public EventOwner {}; + void logEvent(int i) { + events_.push_back(i); + } + + private: + std::vector<int> events_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(EventLoopTest); diff --git a/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp b/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp new file mode 100644 index 0000000..14f24c7 --- /dev/null +++ b/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp @@ -0,0 +1,62 @@ +#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" + +using namespace Swift; + +class SimpleEventLoopTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(SimpleEventLoopTest); + CPPUNIT_TEST(testRun); + CPPUNIT_TEST(testPostFromMainThread); + CPPUNIT_TEST_SUITE_END(); + + public: + SimpleEventLoopTest() {} + + 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_REGISTRATION(SimpleEventLoopTest); |