summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/EventLoop')
-rw-r--r--Swiften/EventLoop/Cocoa/CocoaEvent.h20
-rw-r--r--Swiften/EventLoop/Cocoa/CocoaEvent.mm25
-rw-r--r--Swiften/EventLoop/Cocoa/CocoaEventLoop.h14
-rw-r--r--Swiften/EventLoop/Cocoa/CocoaEventLoop.mm21
-rw-r--r--Swiften/EventLoop/Deleter.h23
-rw-r--r--Swiften/EventLoop/DummyEventLoop.h41
-rw-r--r--Swiften/EventLoop/Event.h22
-rw-r--r--Swiften/EventLoop/EventLoop.cpp49
-rw-r--r--Swiften/EventLoop/EventLoop.h38
-rw-r--r--Swiften/EventLoop/EventOwner.cpp8
-rw-r--r--Swiften/EventLoop/EventOwner.h8
-rw-r--r--Swiften/EventLoop/MainEventLoop.cpp36
-rw-r--r--Swiften/EventLoop/MainEventLoop.h41
-rw-r--r--Swiften/EventLoop/Qt/QtEventLoop.h36
-rw-r--r--Swiften/EventLoop/SConscript17
-rw-r--r--Swiften/EventLoop/SimpleEventLoop.cpp54
-rw-r--r--Swiften/EventLoop/SimpleEventLoop.h30
-rw-r--r--Swiften/EventLoop/UnitTest/EventLoopTest.cpp67
-rw-r--r--Swiften/EventLoop/UnitTest/SimpleEventLoopTest.cpp62
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);