diff options
Diffstat (limited to 'Swiften/EventLoop')
-rw-r--r-- | Swiften/EventLoop/Deleter.h | 23 | ||||
-rw-r--r-- | Swiften/EventLoop/DummyEventLoop.h | 37 | ||||
-rw-r--r-- | Swiften/EventLoop/EventLoop.cpp | 49 | ||||
-rw-r--r-- | Swiften/EventLoop/EventLoop.h | 52 | ||||
-rw-r--r-- | Swiften/EventLoop/MainEventLoop.cpp | 35 | ||||
-rw-r--r-- | Swiften/EventLoop/MainEventLoop.h | 40 | ||||
-rw-r--r-- | Swiften/EventLoop/Makefile.inc | 6 | ||||
-rw-r--r-- | Swiften/EventLoop/SimpleEventLoop.cpp | 48 | ||||
-rw-r--r-- | Swiften/EventLoop/SimpleEventLoop.h | 31 | ||||
-rw-r--r-- | Swiften/EventLoop/UnitTest/EventLoopTest.cpp | 63 | ||||
-rw-r--r-- | Swiften/EventLoop/UnitTest/Makefile.inc | 3 | ||||
-rw-r--r-- | Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp | 62 |
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); |