summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/EventLoop')
-rw-r--r--Swiften/EventLoop/Deleter.h23
-rw-r--r--Swiften/EventLoop/DummyEventLoop.h37
-rw-r--r--Swiften/EventLoop/EventLoop.cpp49
-rw-r--r--Swiften/EventLoop/EventLoop.h52
-rw-r--r--Swiften/EventLoop/MainEventLoop.cpp35
-rw-r--r--Swiften/EventLoop/MainEventLoop.h40
-rw-r--r--Swiften/EventLoop/Makefile.inc6
-rw-r--r--Swiften/EventLoop/SimpleEventLoop.cpp48
-rw-r--r--Swiften/EventLoop/SimpleEventLoop.h31
-rw-r--r--Swiften/EventLoop/UnitTest/EventLoopTest.cpp63
-rw-r--r--Swiften/EventLoop/UnitTest/Makefile.inc3
-rw-r--r--Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp62
12 files changed, 449 insertions, 0 deletions
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..234ecfa
--- /dev/null
+++ b/Swiften/EventLoop/DummyEventLoop.h
@@ -0,0 +1,37 @@
+#ifndef SWIFTEN_DummyEventLoop_H
+#define SWIFTEN_DummyEventLoop_H
+
+#include <deque>
+#include <boost/function.hpp>
+
+#include "Swiften/EventLoop/EventLoop.h"
+#include "Swiften/Base/foreach.h"
+
+namespace Swift {
+ class DummyEventLoop : public EventLoop {
+ public:
+ DummyEventLoop() {
+ }
+
+ 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_;
+ };
+}
+
+#endif
+
diff --git a/Swiften/EventLoop/EventLoop.cpp b/Swiften/EventLoop/EventLoop.cpp
new file mode 100644
index 0000000..cec149c
--- /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, void* 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(void* 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..2f04f32
--- /dev/null
+++ b/Swiften/EventLoop/EventLoop.h
@@ -0,0 +1,52 @@
+#ifndef SWIFTEN_EventLoop_H
+#define SWIFTEN_EventLoop_H
+
+#include <boost/function.hpp>
+#include <boost/thread/mutex.hpp>
+#include <list>
+
+namespace Swift {
+ class EventLoop {
+ public:
+ EventLoop();
+ virtual ~EventLoop();
+
+ void postEvent(boost::function<void ()> event, void* owner);
+ void removeEventsFromOwner(void* owner);
+
+ protected:
+ struct Event {
+ Event(void* owner, const boost::function<void()>& callback) :
+ owner(owner), callback(callback) {
+ }
+
+ bool operator==(const Event& o) const {
+ return o.id == id;
+ }
+
+ unsigned int id;
+ void* owner;
+ boost::function<void()> callback;
+ };
+
+ /**
+ * 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(void* owner) : owner(owner) {}
+ bool operator()(const Event& event) { return event.owner == owner; }
+ void* owner;
+ };
+ boost::mutex eventsMutex_;
+ unsigned int nextEventID_;
+ std::list<Event> events_;
+ };
+}
+
+#endif
diff --git a/Swiften/EventLoop/MainEventLoop.cpp b/Swiften/EventLoop/MainEventLoop.cpp
new file mode 100644
index 0000000..afaab42
--- /dev/null
+++ b/Swiften/EventLoop/MainEventLoop.cpp
@@ -0,0 +1,35 @@
+#include "Swiften/EventLoop/MainEventLoop.h"
+
+#include <iostream>
+
+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, void* owner) {
+ getInstance()->postEvent(event, owner);
+}
+
+void MainEventLoop::removeEventsFromOwner(void* 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..f29dbd4
--- /dev/null
+++ b/Swiften/EventLoop/MainEventLoop.h
@@ -0,0 +1,40 @@
+#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 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, void* owner = 0);
+
+ static void removeEventsFromOwner(void* 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/Makefile.inc b/Swiften/EventLoop/Makefile.inc
new file mode 100644
index 0000000..894b18d
--- /dev/null
+++ b/Swiften/EventLoop/Makefile.inc
@@ -0,0 +1,6 @@
+SWIFTEN_SOURCES += \
+ Swiften/EventLoop/EventLoop.cpp \
+ Swiften/EventLoop/SimpleEventLoop.cpp \
+ Swiften/EventLoop/MainEventLoop.cpp
+
+include Swiften/EventLoop/UnitTest/Makefile.inc
diff --git a/Swiften/EventLoop/SimpleEventLoop.cpp b/Swiften/EventLoop/SimpleEventLoop.cpp
new file mode 100644
index 0000000..96ad774
--- /dev/null
+++ b/Swiften/EventLoop/SimpleEventLoop.cpp
@@ -0,0 +1,48 @@
+#include "Swiften/EventLoop/SimpleEventLoop.h"
+
+#include <boost/bind.hpp>
+
+#include "Swiften/Base/foreach.h"
+
+
+namespace Swift {
+
+void nop() {}
+
+SimpleEventLoop::SimpleEventLoop() : isRunning_(true) {
+}
+
+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), 0);
+}
+
+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..45eaae1
--- /dev/null
+++ b/Swiften/EventLoop/SimpleEventLoop.h
@@ -0,0 +1,31 @@
+#ifndef SWIFTEN_SimpleEventLoop_H
+#define SWIFTEN_SimpleEventLoop_H
+
+#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();
+
+ 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_;
+ };
+}
+#endif
diff --git a/Swiften/EventLoop/UnitTest/EventLoopTest.cpp b/Swiften/EventLoop/UnitTest/EventLoopTest.cpp
new file mode 100644
index 0000000..c64d1ad
--- /dev/null
+++ b/Swiften/EventLoop/UnitTest/EventLoopTest.cpp
@@ -0,0 +1,63 @@
+#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 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), 0);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 2), 0);
+ 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;
+
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 1), &testling);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 2), this);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 3), &testling);
+ testling.postEvent(boost::bind(&EventLoopTest::logEvent, this, 4), this);
+ testling.removeEventsFromOwner(this);
+ 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:
+ void logEvent(int i) {
+ events_.push_back(i);
+ }
+
+ private:
+ std::vector<int> events_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(EventLoopTest);
diff --git a/Swiften/EventLoop/UnitTest/Makefile.inc b/Swiften/EventLoop/UnitTest/Makefile.inc
new file mode 100644
index 0000000..5eec2da
--- /dev/null
+++ b/Swiften/EventLoop/UnitTest/Makefile.inc
@@ -0,0 +1,3 @@
+UNITTEST_SOURCES += \
+ Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp \
+ Swiften/EventLoop/UnitTest/EventLoopTest.cpp
diff --git a/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp b/Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp
new file mode 100644
index 0000000..a39aa9a
--- /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), 0);
+ 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), 0);
+ }
+ loop->stop();
+ }
+
+ void incrementCounter() {
+ counter_++;
+ }
+
+ void incrementCounterAndStop(SimpleEventLoop* loop) {
+ counter_++;
+ loop->stop();
+ }
+
+ int counter_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(SimpleEventLoopTest);