aboutsummaryrefslogtreecommitdiffstats
path: root/src/event
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/event/BufferedSocket.cxx3
-rw-r--r--src/event/BufferedSocket.hxx2
-rw-r--r--src/event/Call.cxx15
-rw-r--r--src/event/DeferredMonitor.cxx16
-rw-r--r--src/event/DeferredMonitor.hxx39
-rw-r--r--src/event/FullyBufferedSocket.cxx1
-rw-r--r--src/event/FullyBufferedSocket.hxx1
-rw-r--r--src/event/IdleMonitor.cxx19
-rw-r--r--src/event/IdleMonitor.hxx24
-rw-r--r--src/event/Loop.cxx46
-rw-r--r--src/event/Loop.hxx58
-rw-r--r--src/event/MultiSocketMonitor.cxx6
-rw-r--r--src/event/MultiSocketMonitor.hxx58
-rw-r--r--src/event/PollGroup.hxx41
-rw-r--r--src/event/PollGroupEPoll.hxx91
-rw-r--r--src/event/PollGroupPoll.cxx88
-rw-r--r--src/event/PollGroupPoll.hxx62
-rw-r--r--src/event/PollGroupWinSelect.cxx161
-rw-r--r--src/event/PollGroupWinSelect.hxx111
-rw-r--r--src/event/PollResultGeneric.hxx57
-rw-r--r--src/event/ServerSocket.cxx34
-rw-r--r--src/event/ServerSocket.hxx3
-rw-r--r--src/event/SignalMonitor.cxx3
-rw-r--r--src/event/SocketMonitor.cxx24
-rw-r--r--src/event/SocketMonitor.hxx61
-rw-r--r--src/event/TimeoutMonitor.cxx22
-rw-r--r--src/event/TimeoutMonitor.hxx28
27 files changed, 897 insertions, 177 deletions
diff --git a/src/event/BufferedSocket.cxx b/src/event/BufferedSocket.cxx
index 92e350e85..0f71bd941 100644
--- a/src/event/BufferedSocket.cxx
+++ b/src/event/BufferedSocket.cxx
@@ -22,6 +22,9 @@
#include "system/SocketError.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
+#include "Compiler.h"
+
+#include <algorithm>
BufferedSocket::ssize_t
BufferedSocket::DirectRead(void *data, size_t length)
diff --git a/src/event/BufferedSocket.hxx b/src/event/BufferedSocket.hxx
index db920f981..9bd1b5b49 100644
--- a/src/event/BufferedSocket.hxx
+++ b/src/event/BufferedSocket.hxx
@@ -23,13 +23,13 @@
#include "check.h"
#include "SocketMonitor.hxx"
#include "util/FifoBuffer.hxx"
-#include "Compiler.h"
#include <assert.h>
#include <stdint.h>
struct fifo_buffer;
class Error;
+class EventLoop;
/**
* A #SocketMonitor specialization that adds an input buffer.
diff --git a/src/event/Call.cxx b/src/event/Call.cxx
index ab1d5ffbd..7767824f9 100644
--- a/src/event/Call.cxx
+++ b/src/event/Call.cxx
@@ -28,7 +28,7 @@
#include <assert.h>
class BlockingCallMonitor final
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
: DeferredMonitor
#endif
{
@@ -40,20 +40,22 @@ class BlockingCallMonitor final
bool done;
public:
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
BlockingCallMonitor(EventLoop &loop, std::function<void()> &&_f)
:f(std::move(_f)), done(false) {
loop.AddCall([this](){
this->DoRun();
});
}
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
BlockingCallMonitor(EventLoop &_loop, std::function<void()> &&_f)
:DeferredMonitor(_loop), f(std::move(_f)), done(false) {}
#endif
void Run() {
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
assert(!done);
Schedule();
@@ -65,13 +67,14 @@ public:
mutex.unlock();
}
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
private:
virtual void RunDeferred() override {
DoRun();
}
+#endif
-#else
+#ifdef USE_INTERNAL_EVENTLOOP
public:
#endif
void DoRun() {
diff --git a/src/event/DeferredMonitor.cxx b/src/event/DeferredMonitor.cxx
index 4ffffaa89..40b4b0b62 100644
--- a/src/event/DeferredMonitor.cxx
+++ b/src/event/DeferredMonitor.cxx
@@ -24,9 +24,10 @@
void
DeferredMonitor::Cancel()
{
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
pending = false;
-#else
+#endif
+#ifdef USE_GLIB_EVENTLOOP
const auto id = source_id.exchange(0);
if (id != 0)
g_source_remove(id);
@@ -36,10 +37,11 @@ DeferredMonitor::Cancel()
void
DeferredMonitor::Schedule()
{
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
if (!pending.exchange(true))
fd.Write();
-#else
+#endif
+#ifdef USE_GLIB_EVENTLOOP
const unsigned id = loop.AddIdle(Callback, this);
const auto old_id = source_id.exchange(id);
if (old_id != 0)
@@ -47,7 +49,7 @@ DeferredMonitor::Schedule()
#endif
}
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
bool
DeferredMonitor::OnSocketReady(unsigned)
@@ -60,7 +62,9 @@ DeferredMonitor::OnSocketReady(unsigned)
return true;
}
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
void
DeferredMonitor::Run()
diff --git a/src/event/DeferredMonitor.hxx b/src/event/DeferredMonitor.hxx
index 2380fb66f..d4c812e44 100644
--- a/src/event/DeferredMonitor.hxx
+++ b/src/event/DeferredMonitor.hxx
@@ -23,10 +23,12 @@
#include "check.h"
#include "Compiler.h"
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
#include "SocketMonitor.hxx"
#include "WakeFD.hxx"
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
#include <glib.h>
#endif
@@ -38,44 +40,51 @@ class EventLoop;
* Defer execution of an event into an #EventLoop.
*/
class DeferredMonitor
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
: private SocketMonitor
#endif
{
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
std::atomic_bool pending;
WakeFD fd;
-#else
- EventLoop &loop;
+#endif
+#ifdef USE_GLIB_EVENTLOOP
+ EventLoop &loop;
std::atomic<guint> source_id;
#endif
public:
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
DeferredMonitor(EventLoop &_loop)
:SocketMonitor(_loop), pending(false) {
SocketMonitor::Open(fd.Get());
SocketMonitor::Schedule(SocketMonitor::READ);
}
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
DeferredMonitor(EventLoop &_loop)
:loop(_loop), source_id(0) {}
#endif
~DeferredMonitor() {
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
/* avoid closing the WakeFD twice */
SocketMonitor::Steal();
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
Cancel();
#endif
}
EventLoop &GetEventLoop() {
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
return SocketMonitor::GetEventLoop();
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
return loop;
#endif
}
@@ -87,9 +96,11 @@ protected:
virtual void RunDeferred() = 0;
private:
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
virtual bool OnSocketReady(unsigned flags) override final;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
void Run();
static gboolean Callback(gpointer data);
#endif
diff --git a/src/event/FullyBufferedSocket.cxx b/src/event/FullyBufferedSocket.cxx
index 8b57b1308..87d29296e 100644
--- a/src/event/FullyBufferedSocket.cxx
+++ b/src/event/FullyBufferedSocket.cxx
@@ -23,6 +23,7 @@
#include "util/fifo_buffer.h"
#include "util/Error.hxx"
#include "util/Domain.hxx"
+#include "Compiler.h"
#include <assert.h>
#include <stdint.h>
diff --git a/src/event/FullyBufferedSocket.hxx b/src/event/FullyBufferedSocket.hxx
index c50bb5f61..083fab15e 100644
--- a/src/event/FullyBufferedSocket.hxx
+++ b/src/event/FullyBufferedSocket.hxx
@@ -24,7 +24,6 @@
#include "BufferedSocket.hxx"
#include "IdleMonitor.hxx"
#include "util/PeakBuffer.hxx"
-#include "Compiler.h"
/**
* A #BufferedSocket specialization that adds an output buffer.
diff --git a/src/event/IdleMonitor.cxx b/src/event/IdleMonitor.cxx
index c99c66b26..45f533e3f 100644
--- a/src/event/IdleMonitor.cxx
+++ b/src/event/IdleMonitor.cxx
@@ -21,6 +21,8 @@
#include "IdleMonitor.hxx"
#include "Loop.hxx"
+#include <assert.h>
+
void
IdleMonitor::Cancel()
{
@@ -29,10 +31,11 @@ IdleMonitor::Cancel()
if (!IsActive())
return;
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
active = false;
loop.RemoveIdle(*this);
-#else
+#endif
+#ifdef USE_GLIB_EVENTLOOP
g_source_remove(source_id);
source_id = 0;
#endif
@@ -47,10 +50,11 @@ IdleMonitor::Schedule()
/* already scheduled */
return;
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
active = true;
loop.AddIdle(*this);
-#else
+#endif
+#ifdef USE_GLIB_EVENTLOOP
source_id = loop.AddIdle(Callback, this);
#endif
}
@@ -60,10 +64,11 @@ IdleMonitor::Run()
{
assert(loop.IsInside());
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
assert(active);
active = false;
-#else
+#endif
+#ifdef USE_GLIB_EVENTLOOP
assert(source_id != 0);
source_id = 0;
#endif
@@ -71,7 +76,7 @@ IdleMonitor::Run()
OnIdle();
}
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
gboolean
IdleMonitor::Callback(gpointer data)
diff --git a/src/event/IdleMonitor.hxx b/src/event/IdleMonitor.hxx
index c8e79eb1d..b040915e7 100644
--- a/src/event/IdleMonitor.hxx
+++ b/src/event/IdleMonitor.hxx
@@ -22,7 +22,7 @@
#include "check.h"
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
#include <glib.h>
#endif
@@ -34,23 +34,27 @@ class EventLoop;
* methods must be run from EventLoop's thread.
*/
class IdleMonitor {
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
friend class EventLoop;
#endif
EventLoop &loop;
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
bool active;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
guint source_id;
#endif
public:
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
IdleMonitor(EventLoop &_loop)
:loop(_loop), active(false) {}
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
IdleMonitor(EventLoop &_loop)
:loop(_loop), source_id(0) {}
#endif
@@ -64,9 +68,11 @@ public:
}
bool IsActive() const {
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
return active;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
return source_id != 0;
#endif
}
@@ -79,7 +85,7 @@ protected:
private:
void Run();
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
static gboolean Callback(gpointer data);
#endif
};
diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx
index 5aa24aea2..f7b3df022 100644
--- a/src/event/Loop.cxx
+++ b/src/event/Loop.cxx
@@ -20,7 +20,7 @@
#include "config.h"
#include "Loop.hxx"
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
#include "system/Clock.hxx"
#include "TimeoutMonitor.hxx"
@@ -33,7 +33,6 @@ EventLoop::EventLoop(Default)
:SocketMonitor(*this),
now_ms(::MonotonicClockMS()),
quit(false),
- n_events(0),
thread(ThreadId::Null())
{
SocketMonitor::Open(wake_fd.Get());
@@ -58,19 +57,18 @@ EventLoop::Break()
AddCall([this]() { Break(); });
}
-void
-EventLoop::Abandon(SocketMonitor &m)
+bool
+EventLoop::Abandon(int _fd, SocketMonitor &m)
{
- for (unsigned i = 0, n = n_events; i < n; ++i)
- if (events[i].data.ptr == &m)
- events[i].events = 0;
+ poll_result.Clear(&m);
+ return poll_group.Abandon(_fd);
}
bool
EventLoop::RemoveFD(int _fd, SocketMonitor &m)
{
- Abandon(m);
- return epoll.Remove(_fd);
+ poll_result.Clear(&m);
+ return poll_group.Remove(_fd);
}
void
@@ -115,7 +113,7 @@ EventLoop::Run()
assert(thread.IsNull());
thread = ThreadId::GetCurrent();
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
assert(!quit);
do {
@@ -164,37 +162,37 @@ EventLoop::Run()
/* wait for new event */
- const int n = epoll.Wait(events, MAX_EVENTS, timeout_ms);
- n_events = std::max(n, 0);
+ poll_group.ReadEvents(poll_result, timeout_ms);
now_ms = ::MonotonicClockMS();
assert(!quit);
/* invoke sockets */
-
- for (int i = 0; i < n; ++i) {
- const auto &e = events[i];
-
- if (e.events != 0) {
- SocketMonitor &m = *(SocketMonitor *)e.data.ptr;
- m.Dispatch(e.events);
+ for (int i = 0; i < poll_result.GetSize(); ++i) {
+ auto events = poll_result.GetEvents(i);
+ if (events != 0) {
+ auto m = (SocketMonitor *)poll_result.GetObject(i);
+ m->Dispatch(events);
if (quit)
break;
}
}
- n_events = 0;
+ poll_result.Reset();
+
} while (!quit);
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
g_main_loop_run(loop);
#endif
assert(thread.IsInside());
}
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
void
EventLoop::AddCall(std::function<void()> &&f)
@@ -229,7 +227,9 @@ EventLoop::OnSocketReady(gcc_unused unsigned flags)
return true;
}
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
guint
EventLoop::AddIdle(GSourceFunc function, gpointer data)
diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx
index 62e733747..029d01245 100644
--- a/src/event/Loop.hxx
+++ b/src/event/Loop.hxx
@@ -24,8 +24,8 @@
#include "thread/Id.hxx"
#include "Compiler.h"
-#ifdef USE_EPOLL
-#include "system/EPollFD.hxx"
+#ifdef USE_INTERNAL_EVENTLOOP
+#include "PollGroup.hxx"
#include "thread/Mutex.hxx"
#include "WakeFD.hxx"
#include "SocketMonitor.hxx"
@@ -33,11 +33,13 @@
#include <functional>
#include <list>
#include <set>
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
#include <glib.h>
#endif
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
class TimeoutMonitor;
class IdleMonitor;
class SocketMonitor;
@@ -45,12 +47,21 @@ class SocketMonitor;
#include <assert.h>
+/**
+ * An event loop that polls for events on file/socket descriptors.
+ *
+ * This class is not thread-safe, all methods must be called from the
+ * thread that runs it, except where explicitly documented as
+ * thread-safe.
+ *
+ * @see SocketMonitor, MultiSocketMonitor, TimeoutMonitor, IdleMonitor
+ */
class EventLoop final
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
: private SocketMonitor
#endif
{
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
struct TimerRecord {
/**
* Projected monotonic_clock_ms() value when this
@@ -73,8 +84,6 @@ class EventLoop final
}
};
- EPollFD epoll;
-
WakeFD wake_fd;
std::multiset<TimerRecord> timers;
@@ -87,10 +96,11 @@ class EventLoop final
bool quit;
- static constexpr unsigned MAX_EVENTS = 16;
- unsigned n_events;
- epoll_event events[MAX_EVENTS];
-#else
+ PollGroup poll_group;
+ PollResult poll_result;
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
GMainContext *context;
GMainLoop *loop;
#endif
@@ -101,24 +111,32 @@ class EventLoop final
ThreadId thread;
public:
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
struct Default {};
EventLoop(Default dummy=Default());
~EventLoop();
+ /**
+ * A caching wrapper for MonotonicClockMS().
+ */
unsigned GetTimeMS() const {
return now_ms;
}
+ /**
+ * Stop execution of this #EventLoop at the next chance. This
+ * method is thread-safe and non-blocking: after returning, it
+ * is not guaranteed that the EventLoop has really stopped.
+ */
void Break();
bool AddFD(int _fd, unsigned flags, SocketMonitor &m) {
- return epoll.Add(_fd, flags, &m);
+ return poll_group.Add(_fd, flags, &m);
}
bool ModifyFD(int _fd, unsigned flags, SocketMonitor &m) {
- return epoll.Modify(_fd, flags, &m);
+ return poll_group.Modify(_fd, flags, &m);
}
/**
@@ -126,7 +144,7 @@ public:
* has been closed. This is like RemoveFD(), but does not
* attempt to use #EPOLL_CTL_DEL.
*/
- void Abandon(SocketMonitor &m);
+ bool Abandon(int fd, SocketMonitor &m);
bool RemoveFD(int fd, SocketMonitor &m);
@@ -138,13 +156,19 @@ public:
void AddCall(std::function<void()> &&f);
+ /**
+ * The main function of this class. It will loop until
+ * Break() gets called. Can be called only once.
+ */
void Run();
private:
virtual bool OnSocketReady(unsigned flags) override;
public:
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
EventLoop()
:context(g_main_context_new()),
loop(g_main_loop_new(context, false)),
diff --git a/src/event/MultiSocketMonitor.cxx b/src/event/MultiSocketMonitor.cxx
index bd1aa6fef..f26ab6c28 100644
--- a/src/event/MultiSocketMonitor.cxx
+++ b/src/event/MultiSocketMonitor.cxx
@@ -25,7 +25,7 @@
#include <assert.h>
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop)
:IdleMonitor(_loop), TimeoutMonitor(_loop), ready(false) {
@@ -65,7 +65,9 @@ MultiSocketMonitor::OnIdle()
}
}
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
/**
* The vtable for our GSource implementation. Unfortunately, we
diff --git a/src/event/MultiSocketMonitor.hxx b/src/event/MultiSocketMonitor.hxx
index 8ee81a508..680930037 100644
--- a/src/event/MultiSocketMonitor.hxx
+++ b/src/event/MultiSocketMonitor.hxx
@@ -23,22 +23,25 @@
#include "check.h"
#include "Compiler.h"
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
#include "IdleMonitor.hxx"
#include "TimeoutMonitor.hxx"
#include "SocketMonitor.hxx"
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
#include <glib.h>
#endif
#include <forward_list>
+#include <iterator>
#include <assert.h>
#include <stdint.h>
#ifdef WIN32
-/* ERRORis a WIN32 macro that poisons our namespace; this is a
- kludge to allow us to use it anyway */
+/* ERROR is a WIN32 macro that poisons our namespace; this is a kludge
+ to allow us to use it anyway */
#ifdef ERROR
#undef ERROR
#endif
@@ -47,14 +50,17 @@
class EventLoop;
/**
- * Monitor multiple sockets.
+ * Similar to #SocketMonitor, but monitors multiple sockets. To use
+ * it, implement the methods PrepareSockets() and DispatchSockets().
+ * In PrepareSockets(), use UpdateSocketList() and AddSocket().
+ * DispatchSockets() will be called if at least one socket is ready.
*/
class MultiSocketMonitor
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
: private IdleMonitor, private TimeoutMonitor
#endif
{
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
class SingleFD final : public SocketMonitor {
MultiSocketMonitor &multi;
@@ -99,7 +105,9 @@ class MultiSocketMonitor
friend class SingleFD;
bool ready, refresh;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
struct Source {
GSource base;
@@ -138,12 +146,14 @@ class MultiSocketMonitor
std::forward_list<SingleFD> fds;
public:
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
static constexpr unsigned READ = SocketMonitor::READ;
static constexpr unsigned WRITE = SocketMonitor::WRITE;
static constexpr unsigned ERROR = SocketMonitor::ERROR;
static constexpr unsigned HANGUP = SocketMonitor::HANGUP;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
static constexpr unsigned READ = G_IO_IN;
static constexpr unsigned WRITE = G_IO_OUT;
static constexpr unsigned ERROR = G_IO_ERR;
@@ -153,16 +163,18 @@ public:
MultiSocketMonitor(EventLoop &_loop);
~MultiSocketMonitor();
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
using IdleMonitor::GetEventLoop;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
EventLoop &GetEventLoop() {
return loop;
}
#endif
public:
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
gcc_pure
uint64_t GetTime() const {
return g_source_get_time(&source->base);
@@ -170,10 +182,12 @@ public:
#endif
void InvalidateSockets() {
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
refresh = true;
IdleMonitor::Schedule();
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
/* no-op because GLib always calls the GSource's
"prepare" method before each poll() anyway */
#endif
@@ -181,7 +195,7 @@ public:
void AddSocket(int fd, unsigned events) {
fds.emplace_front(*this, fd, events);
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
g_source_add_poll(&source->base, &fds.front().pfd);
#endif
}
@@ -198,9 +212,11 @@ public:
i->SetEvents(events);
prev = i;
} else {
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
i->Steal();
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
g_source_remove_poll(&source->base, &i->pfd);
#endif
fds.erase_after(prev);
@@ -215,7 +231,7 @@ protected:
virtual int PrepareSockets() = 0;
virtual void DispatchSockets() = 0;
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
private:
void SetReady() {
ready = true;
@@ -231,7 +247,9 @@ private:
virtual void OnIdle() final;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
public:
/* GSource callbacks */
static gboolean Prepare(GSource *source, gint *timeout_r);
diff --git a/src/event/PollGroup.hxx b/src/event/PollGroup.hxx
new file mode 100644
index 000000000..038ffc13e
--- /dev/null
+++ b/src/event/PollGroup.hxx
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_EVENT_POLLGROUP_HXX
+#define MPD_EVENT_POLLGROUP_HXX
+
+#ifdef USE_EPOLL
+#include "PollGroupEPoll.hxx"
+typedef PollResultEPoll PollResult;
+typedef PollGroupEPoll PollGroup;
+#endif
+
+#ifdef USE_WINSELECT
+#include "PollGroupWinSelect.hxx"
+typedef PollResultGeneric PollResult;
+typedef PollGroupWinSelect PollGroup;
+#endif
+
+#ifdef USE_POLL
+#include "PollGroupPoll.hxx"
+typedef PollResultGeneric PollResult;
+typedef PollGroupPoll PollGroup;
+#endif
+
+#endif
diff --git a/src/event/PollGroupEPoll.hxx b/src/event/PollGroupEPoll.hxx
new file mode 100644
index 000000000..21bb3a322
--- /dev/null
+++ b/src/event/PollGroupEPoll.hxx
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_EVENT_POLLGROUP_EPOLL_HXX
+#define MPD_EVENT_POLLGROUP_EPOLL_HXX
+
+#include "check.h"
+
+#include "Compiler.h"
+#include "system/EPollFD.hxx"
+
+#include <array>
+#include <algorithm>
+
+class PollResultEPoll
+{
+ friend class PollGroupEPoll;
+
+ std::array<epoll_event, 16> events;
+ int n_events;
+public:
+ PollResultEPoll() : n_events(0) { }
+
+ int GetSize() const { return n_events; }
+ unsigned GetEvents(int i) const { return events[i].events; }
+ void *GetObject(int i) const { return events[i].data.ptr; }
+ void Reset() { n_events = 0; }
+
+ void Clear(void *obj) {
+ for (int i = 0; i < n_events; ++i)
+ if (events[i].data.ptr == obj)
+ events[i].events = 0;
+ }
+};
+
+class PollGroupEPoll
+{
+ EPollFD epoll;
+
+ PollGroupEPoll(PollGroupEPoll &) = delete;
+ PollGroupEPoll &operator=(PollGroupEPoll &) = delete;
+public:
+ static constexpr unsigned READ = EPOLLIN;
+ static constexpr unsigned WRITE = EPOLLOUT;
+ static constexpr unsigned ERROR = EPOLLERR;
+ static constexpr unsigned HANGUP = EPOLLHUP;
+
+ PollGroupEPoll() = default;
+
+ void ReadEvents(PollResultEPoll &result, int timeout_ms) {
+ int ret = epoll.Wait(result.events.data(), result.events.size(),
+ timeout_ms);
+ result.n_events = std::max(0, ret);
+ }
+
+ bool Add(int fd, unsigned events, void *obj) {
+ return epoll.Add(fd, events, obj);
+ }
+
+ bool Modify(int fd, unsigned events, void *obj) {
+ return epoll.Modify(fd, events, obj);
+ }
+
+ bool Remove(int fd) {
+ return epoll.Remove(fd);
+ }
+
+ bool Abandon(gcc_unused int fd) {
+ // Nothing to do in this implementation.
+ // Closed descriptors are automatically unregistered.
+ return true;
+ }
+};
+
+#endif
diff --git a/src/event/PollGroupPoll.cxx b/src/event/PollGroupPoll.cxx
new file mode 100644
index 000000000..a2b4d1d88
--- /dev/null
+++ b/src/event/PollGroupPoll.cxx
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#ifdef USE_POLL
+
+#include "PollGroupPoll.hxx"
+
+#include <assert.h>
+
+PollGroupPoll::PollGroupPoll() { }
+PollGroupPoll::~PollGroupPoll() { }
+
+bool PollGroupPoll::Add(int fd, unsigned events, void *obj)
+{
+ assert(items.find(fd) == items.end());
+ poll_events.resize(poll_events.size() + 1);
+ size_t index = poll_events.size() - 1;
+ auto &e = poll_events[index];
+ e.fd = fd;
+ e.events = events;
+ e.revents = 0;
+ auto &item = items[fd];
+ item.index = index;
+ item.obj = obj;
+ return true;
+}
+
+bool PollGroupPoll::Modify(int fd, unsigned events, void *obj)
+{
+ auto item_iter = items.find(fd);
+ assert(item_iter != items.end());
+ auto &item = item_iter->second;
+ item.obj = obj;
+ auto &e = poll_events[item.index];
+ e.events = events;
+ e.revents &= events;
+ return true;
+}
+
+bool PollGroupPoll::Remove(int fd)
+{
+ auto item_iter = items.find(fd);
+ assert(item_iter != items.end());
+ auto &item = item_iter->second;
+ size_t index = item.index;
+ size_t last_index = poll_events.size() - 1;
+ if (index != last_index) {
+ std::swap(poll_events[index], poll_events[last_index]);
+ items[poll_events[index].fd].index = index;
+ }
+ poll_events.pop_back();
+ items.erase(item_iter);
+ return true;
+}
+
+void PollGroupPoll::ReadEvents(PollResultGeneric &result, int timeout_ms)
+{
+ int n = poll(poll_events.empty() ? nullptr : &poll_events[0],
+ poll_events.size(), timeout_ms);
+
+ for (size_t i = 0; n > 0 && i < poll_events.size(); ++i) {
+ const auto &e = poll_events[i];
+ if (e.revents != 0) {
+ result.Add(e.revents, items[e.fd].obj);
+ --n;
+ }
+ }
+}
+
+#endif
diff --git a/src/event/PollGroupPoll.hxx b/src/event/PollGroupPoll.hxx
new file mode 100644
index 000000000..e8850ebb5
--- /dev/null
+++ b/src/event/PollGroupPoll.hxx
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_EVENT_POLLGROUP_POLL_HXX
+#define MPD_EVENT_POLLGROUP_POLL_HXX
+
+#include "check.h"
+#include "PollResultGeneric.hxx"
+
+#include <string.h> // for size_t
+#include <vector>
+#include <unordered_map>
+#include <sys/poll.h>
+
+class PollGroupPoll
+{
+ struct Item
+ {
+ size_t index;
+ void *obj;
+ };
+
+ std::vector<pollfd> poll_events;
+ std::unordered_map<int, Item> items;
+
+ PollGroupPoll(PollGroupPoll &) = delete;
+ PollGroupPoll &operator=(PollGroupPoll &) = delete;
+public:
+ static constexpr unsigned READ = POLLIN;
+ static constexpr unsigned WRITE = POLLOUT;
+ static constexpr unsigned ERROR = POLLERR;
+ static constexpr unsigned HANGUP = POLLHUP;
+
+ PollGroupPoll();
+ ~PollGroupPoll();
+
+ void ReadEvents(PollResultGeneric &result, int timeout_ms);
+ bool Add(int fd, unsigned events, void *obj);
+ bool Modify(int fd, unsigned events, void *obj);
+ bool Remove(int fd);
+ bool Abandon(int fd) {
+ return Remove(fd);
+ }
+};
+
+#endif
diff --git a/src/event/PollGroupWinSelect.cxx b/src/event/PollGroupWinSelect.cxx
new file mode 100644
index 000000000..b184ff2b2
--- /dev/null
+++ b/src/event/PollGroupWinSelect.cxx
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#ifdef USE_WINSELECT
+
+#include "PollGroupWinSelect.hxx"
+
+constexpr int EVENT_READ = 0;
+constexpr int EVENT_WRITE = 1;
+
+static inline bool HasEvent(unsigned events, int event_id)
+{
+ return (events & (1 << event_id)) != 0;
+}
+
+PollGroupWinSelect::PollGroupWinSelect() { }
+PollGroupWinSelect::~PollGroupWinSelect() { }
+
+bool PollGroupWinSelect::CanModify(PollGroupWinSelect::Item &item,
+ unsigned events, int event_id)
+{
+ if (item.index[event_id] < 0 && HasEvent(events, event_id))
+ return !event_set[event_id].IsFull();
+ return true;
+}
+
+void PollGroupWinSelect::Modify(PollGroupWinSelect::Item &item, int fd,
+ unsigned events, int event_id)
+{
+ int index = item.index[event_id];
+ auto &set = event_set[event_id];
+
+ if (index < 0 && HasEvent(events, event_id))
+ item.index[event_id] = set.Add(fd);
+ else if (index >= 0 && !HasEvent(events, event_id)) {
+ if (index != set.Size() - 1) {
+ set.MoveToEnd(index);
+ items[set[index]].index[event_id] = index;
+ }
+ set.RemoveLast();
+ item.index[event_id] = -1;
+ }
+}
+
+bool PollGroupWinSelect::Add(int fd, unsigned events, void *obj)
+{
+ assert(items.find(fd) == items.end());
+ auto &item = items[fd];
+
+ item.index[EVENT_READ] = -1;
+ item.index[EVENT_WRITE] = -1;
+ item.obj = obj;
+ item.events = 0;
+
+ if (!CanModify(item, events, EVENT_READ)) {
+ items.erase(fd);
+ return false;
+ }
+ if (!CanModify(item, events, EVENT_WRITE)) {
+ items.erase(fd);
+ return false;
+ }
+
+ Modify(item, fd, events, EVENT_READ);
+ Modify(item, fd, events, EVENT_WRITE);
+ return true;
+}
+
+bool PollGroupWinSelect::Modify(int fd, unsigned events, void *obj)
+{
+ auto item_iter = items.find(fd);
+ assert(item_iter != items.end());
+ auto &item = item_iter->second;
+
+ if (!CanModify(item, events, EVENT_READ))
+ return false;
+ if (!CanModify(item, events, EVENT_WRITE))
+ return false;
+
+ item.obj = obj;
+ Modify(item, fd, events, EVENT_READ);
+ Modify(item, fd, events, EVENT_WRITE);
+ return true;
+}
+
+bool PollGroupWinSelect::Remove(int fd)
+{
+ auto item_iter = items.find(fd);
+ assert(item_iter != items.end());
+ auto &item = item_iter->second;
+
+ Modify(item, fd, 0, EVENT_READ);
+ Modify(item, fd, 0, EVENT_WRITE);
+ items.erase(item_iter);
+ return true;
+}
+
+void PollGroupWinSelect::ReadEvents(PollResultGeneric &result, int timeout_ms)
+{
+ bool use_sleep = event_set[EVENT_READ].IsEmpty() &&
+ event_set[EVENT_WRITE].IsEmpty();
+
+ if (use_sleep) {
+ Sleep(timeout_ms < 0 ? INFINITE : (DWORD) timeout_ms);
+ return;
+ }
+
+ SocketSet read_set(event_set[EVENT_READ]);
+ SocketSet write_set(event_set[EVENT_WRITE]);
+ SocketSet except_set(event_set[EVENT_WRITE]);
+
+ timeval tv;
+ if (timeout_ms >= 0) {
+ tv.tv_sec = timeout_ms / 1000;
+ tv.tv_usec = (timeout_ms % 1000) * 1000;
+ }
+
+ int ret = select(0,
+ read_set.IsEmpty() ? nullptr : read_set.GetPtr(),
+ write_set.IsEmpty() ? nullptr : write_set.GetPtr(),
+ except_set.IsEmpty() ? nullptr : except_set.GetPtr(),
+ timeout_ms < 0 ? nullptr : &tv);
+
+ if (ret == 0 || ret == SOCKET_ERROR)
+ return;
+
+ for (int i = 0; i < read_set.Size(); ++i)
+ items[read_set[i]].events |= READ;
+
+ for (int i = 0; i < write_set.Size(); ++i)
+ items[write_set[i]].events |= WRITE;
+
+ for (int i = 0; i < except_set.Size(); ++i)
+ items[except_set[i]].events |= WRITE;
+
+ for (auto i = items.begin(); i != items.end(); ++i)
+ if (i->second.events != 0) {
+ result.Add(i->second.events, i->second.obj);
+ i->second.events = 0;
+ }
+}
+
+#endif
diff --git a/src/event/PollGroupWinSelect.hxx b/src/event/PollGroupWinSelect.hxx
new file mode 100644
index 000000000..7a6d12d8a
--- /dev/null
+++ b/src/event/PollGroupWinSelect.hxx
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_EVENT_POLLGROUP_WINSELECT_HXX
+#define MPD_EVENT_POLLGROUP_WINSELECT_HXX
+
+#include "check.h"
+
+#include "PollResultGeneric.hxx"
+
+#include <assert.h>
+#include <string.h>
+
+#include <unordered_map>
+
+#include <windows.h>
+#include <winsock2.h>
+
+#ifdef ERROR
+#undef ERROR
+#endif
+
+class SocketSet
+{
+ fd_set set;
+public:
+ SocketSet() { set.fd_count = 0; }
+ SocketSet(SocketSet &other) {
+ set.fd_count = other.set.fd_count;
+ memcpy(set.fd_array,
+ other.set.fd_array,
+ sizeof (SOCKET) * set.fd_count);
+ }
+
+ fd_set *GetPtr() { return &set; }
+ int Size() { return set.fd_count; }
+ bool IsEmpty() { return set.fd_count == 0; }
+ bool IsFull() { return set.fd_count == FD_SETSIZE; }
+
+ int operator[](int index) {
+ assert(index >= 0 && (u_int)index < set.fd_count);
+ return set.fd_array[index];
+ }
+
+ int Add(int fd) {
+ assert(!IsFull());
+ set.fd_array[set.fd_count] = fd;
+ return set.fd_count++;
+ }
+
+ void MoveToEnd(int index) {
+ assert(index >= 0 && (u_int)index < set.fd_count);
+ std::swap(set.fd_array[index], set.fd_array[set.fd_count - 1]);
+ }
+
+ void RemoveLast() {
+ assert(!IsEmpty());
+ --set.fd_count;
+ }
+};
+
+class PollGroupWinSelect
+{
+ struct Item
+ {
+ int index[2];
+ void *obj;
+ unsigned events;
+ };
+
+ SocketSet event_set[2];
+ std::unordered_map<int, Item> items;
+
+ bool CanModify(Item &item, unsigned events, int event_id);
+ void Modify(Item &item, int fd, unsigned events, int event_id);
+
+ PollGroupWinSelect(PollGroupWinSelect &) = delete;
+ PollGroupWinSelect &operator=(PollGroupWinSelect &) = delete;
+public:
+ static constexpr unsigned READ = 1;
+ static constexpr unsigned WRITE = 2;
+ static constexpr unsigned ERROR = 0;
+ static constexpr unsigned HANGUP = 0;
+
+ PollGroupWinSelect();
+ ~PollGroupWinSelect();
+
+ void ReadEvents(PollResultGeneric &result, int timeout_ms);
+ bool Add(int fd, unsigned events, void *obj);
+ bool Modify(int fd, unsigned events, void *obj);
+ bool Remove(int fd);
+ bool Abandon(int fd) { return Remove(fd); }
+};
+
+#endif
diff --git a/src/event/PollResultGeneric.hxx b/src/event/PollResultGeneric.hxx
new file mode 100644
index 000000000..1c2c0d00b
--- /dev/null
+++ b/src/event/PollResultGeneric.hxx
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef MPD_EVENT_POLLRESULT_GENERIC_HXX
+#define MPD_EVENT_POLLRESULT_GENERIC_HXX
+
+#include "check.h"
+
+#include <vector>
+
+class PollResultGeneric
+{
+ struct Item
+ {
+ unsigned events;
+ void *obj;
+
+ Item() = default;
+ Item(unsigned _events, void *_obj)
+ : events(_events), obj(_obj) { }
+ };
+
+ std::vector<Item> items;
+public:
+ int GetSize() const { return items.size(); }
+ unsigned GetEvents(int i) const { return items[i].events; }
+ void *GetObject(int i) const { return items[i].obj; }
+ void Reset() { items.clear(); }
+
+ void Clear(void *obj) {
+ for (auto i = items.begin(); i != items.end(); ++i)
+ if (i->obj == obj)
+ i->events = 0;
+ }
+
+ void Add(unsigned events, void *obj) {
+ items.emplace_back(events, obj);
+ }
+};
+
+#endif
diff --git a/src/event/ServerSocket.cxx b/src/event/ServerSocket.cxx
index 781d29181..a05d7bde2 100644
--- a/src/event/ServerSocket.cxx
+++ b/src/event/ServerSocket.cxx
@@ -38,6 +38,7 @@
#include <glib.h>
#include <string>
+#include <algorithm>
#include <sys/types.h>
#include <sys/stat.h>
@@ -106,7 +107,10 @@ public:
using SocketMonitor::IsDefined;
using SocketMonitor::Close;
- char *ToString() const;
+ gcc_pure
+ std::string ToString() const {
+ return sockaddr_to_string(address, address_length);
+ }
void SetFD(int _fd) {
SocketMonitor::Open(_fd);
@@ -121,18 +125,6 @@ private:
static constexpr Domain server_socket_domain("server_socket");
-/**
- * Wraper for sockaddr_to_string() which never fails.
- */
-char *
-OneServerSocket::ToString() const
-{
- char *p = sockaddr_to_string(address, address_length, IgnoreError());
- if (p == nullptr)
- p = g_strdup("[unknown]");
- return p;
-}
-
static int
get_remote_uid(int fd)
{
@@ -242,23 +234,21 @@ ServerSocket::Open(Error &error)
Error error2;
if (!i.Open(error2)) {
if (good != nullptr && good->GetSerial() == i.GetSerial()) {
- char *address_string = i.ToString();
- char *good_string = good->ToString();
+ const auto address_string = i.ToString();
+ const auto good_string = good->ToString();
FormatWarning(server_socket_domain,
"bind to '%s' failed: %s "
"(continuing anyway, because "
"binding to '%s' succeeded)",
- address_string, error2.GetMessage(),
- good_string);
- g_free(address_string);
- g_free(good_string);
+ address_string.c_str(),
+ error2.GetMessage(),
+ good_string.c_str());
} else if (bad == nullptr) {
bad = &i;
- char *address_string = i.ToString();
+ const auto address_string = i.ToString();
error2.FormatPrefix("Failed to bind to '%s': ",
- address_string);
- g_free(address_string);
+ address_string.c_str());
last_error = std::move(error2);
}
diff --git a/src/event/ServerSocket.hxx b/src/event/ServerSocket.hxx
index facb10371..05e13ff63 100644
--- a/src/event/ServerSocket.hxx
+++ b/src/event/ServerSocket.hxx
@@ -36,6 +36,9 @@ typedef void (*server_socket_callback_t)(int fd,
class OneServerSocket;
+/**
+ * A socket that accepts incoming stream connections (e.g. TCP).
+ */
class ServerSocket {
friend class OneServerSocket;
diff --git a/src/event/SignalMonitor.cxx b/src/event/SignalMonitor.cxx
index 8c8527a77..bb75f49a3 100644
--- a/src/event/SignalMonitor.cxx
+++ b/src/event/SignalMonitor.cxx
@@ -39,6 +39,9 @@
#include <algorithm>
+#include <assert.h>
+#include <signal.h>
+
class SignalMonitor final : private SocketMonitor {
#ifdef USE_SIGNALFD
SignalFD fd;
diff --git a/src/event/SocketMonitor.cxx b/src/event/SocketMonitor.cxx
index 2b97059f7..769efba82 100644
--- a/src/event/SocketMonitor.cxx
+++ b/src/event/SocketMonitor.cxx
@@ -28,11 +28,10 @@
#ifdef WIN32
#include <winsock2.h>
#else
-#include <sys/types.h>
#include <sys/socket.h>
#endif
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
void
SocketMonitor::Dispatch(unsigned flags)
@@ -43,7 +42,9 @@ SocketMonitor::Dispatch(unsigned flags)
Cancel();
}
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
/*
* GSource methods
@@ -113,14 +114,14 @@ void
SocketMonitor::Open(int _fd)
{
assert(fd < 0);
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
assert(source == nullptr);
#endif
assert(_fd >= 0);
fd = _fd;
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
poll = {fd, 0, 0};
source = (Source *)g_source_new(&socket_monitor_source_funcs,
@@ -142,7 +143,7 @@ SocketMonitor::Steal()
int result = fd;
fd = -1;
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
g_source_destroy(&source->base);
g_source_unref(&source->base);
source = nullptr;
@@ -156,9 +157,10 @@ SocketMonitor::Abandon()
{
assert(IsDefined());
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
+ int old_fd = fd;
fd = -1;
- loop.Abandon(*this);
+ loop.Abandon(old_fd, *this);
#else
Steal();
#endif
@@ -178,7 +180,7 @@ SocketMonitor::Schedule(unsigned flags)
if (flags == GetScheduledFlags())
return;
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
if (scheduled_flags == 0)
loop.AddFD(fd, flags, *this);
else if (flags == 0)
@@ -187,7 +189,9 @@ SocketMonitor::Schedule(unsigned flags)
loop.ModifyFD(fd, flags, *this);
scheduled_flags = flags;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
poll.events = flags;
poll.revents &= flags;
diff --git a/src/event/SocketMonitor.hxx b/src/event/SocketMonitor.hxx
index 5369ddb8a..f6aac2bd6 100644
--- a/src/event/SocketMonitor.hxx
+++ b/src/event/SocketMonitor.hxx
@@ -22,9 +22,11 @@
#include "check.h"
-#ifdef USE_EPOLL
-#include <sys/epoll.h>
-#else
+#ifdef USE_INTERNAL_EVENTLOOP
+#include "PollGroup.hxx"
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
#include <glib.h>
#endif
@@ -34,8 +36,8 @@
#include <stddef.h>
#ifdef WIN32
-/* ERRORis a WIN32 macro that poisons our namespace; this is a
- kludge to allow us to use it anyway */
+/* ERROR is a WIN32 macro that poisons our namespace; this is a kludge
+ to allow us to use it anyway */
#ifdef ERROR
#undef ERROR
#endif
@@ -43,9 +45,14 @@
class EventLoop;
+/**
+ * Monitor events on a socket. Call Schedule() to announce events
+ * you're interested in, or Cancel() to cancel your subscription. The
+ * #EventLoop will invoke virtual method OnSocketReady() as soon as
+ * any of the subscribed events are ready.
+ */
class SocketMonitor {
-#ifdef USE_EPOLL
-#else
+#ifdef USE_GLIB_EVENTLOOP
struct Source {
GSource base;
@@ -56,38 +63,44 @@ class SocketMonitor {
int fd;
EventLoop &loop;
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
/**
* A bit mask of events that is currently registered in the EventLoop.
*/
unsigned scheduled_flags;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
Source *source;
GPollFD poll;
#endif
public:
-#ifdef USE_EPOLL
- static constexpr unsigned READ = EPOLLIN;
- static constexpr unsigned WRITE = EPOLLOUT;
- static constexpr unsigned ERROR = EPOLLERR;
- static constexpr unsigned HANGUP = EPOLLHUP;
-#else
+#ifdef USE_INTERNAL_EVENTLOOP
+ static constexpr unsigned READ = PollGroup::READ;
+ static constexpr unsigned WRITE = PollGroup::WRITE;
+ static constexpr unsigned ERROR = PollGroup::ERROR;
+ static constexpr unsigned HANGUP = PollGroup::HANGUP;
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
static constexpr unsigned READ = G_IO_IN;
static constexpr unsigned WRITE = G_IO_OUT;
static constexpr unsigned ERROR = G_IO_ERR;
static constexpr unsigned HANGUP = G_IO_HUP;
-#endif
+#endif
typedef std::make_signed<size_t>::type ssize_t;
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
SocketMonitor(EventLoop &_loop)
:fd(-1), loop(_loop), scheduled_flags(0) {}
SocketMonitor(int _fd, EventLoop &_loop)
:fd(_fd), loop(_loop), scheduled_flags(0) {}
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
SocketMonitor(EventLoop &_loop)
:fd(-1), loop(_loop), source(nullptr) {}
@@ -128,9 +141,11 @@ public:
unsigned GetScheduledFlags() const {
assert(IsDefined());
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
return scheduled_flags;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
return poll.events;
#endif
}
@@ -167,9 +182,11 @@ protected:
virtual bool OnSocketReady(unsigned flags) = 0;
public:
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
void Dispatch(unsigned flags);
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
/* GSource callbacks */
static gboolean Prepare(GSource *source, gint *timeout_r);
static gboolean Check(GSource *source);
diff --git a/src/event/TimeoutMonitor.cxx b/src/event/TimeoutMonitor.cxx
index cffad6b92..3d7d46324 100644
--- a/src/event/TimeoutMonitor.cxx
+++ b/src/event/TimeoutMonitor.cxx
@@ -25,10 +25,12 @@ void
TimeoutMonitor::Cancel()
{
if (IsActive()) {
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
active = false;
loop.CancelTimer(*this);
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
g_source_destroy(source);
g_source_unref(source);
source = nullptr;
@@ -41,10 +43,12 @@ TimeoutMonitor::Schedule(unsigned ms)
{
Cancel();
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
active = true;
loop.AddTimer(*this, ms);
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
source = loop.AddTimeout(ms, Callback, this);
#endif
}
@@ -54,9 +58,11 @@ TimeoutMonitor::ScheduleSeconds(unsigned s)
{
Cancel();
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
Schedule(s * 1000u);
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
source = loop.AddTimeoutSeconds(s, Callback, this);
#endif
}
@@ -64,14 +70,14 @@ TimeoutMonitor::ScheduleSeconds(unsigned s)
void
TimeoutMonitor::Run()
{
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
Cancel();
#endif
OnTimeout();
}
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
gboolean
TimeoutMonitor::Callback(gpointer data)
diff --git a/src/event/TimeoutMonitor.hxx b/src/event/TimeoutMonitor.hxx
index 98e4e5564..568aa27ef 100644
--- a/src/event/TimeoutMonitor.hxx
+++ b/src/event/TimeoutMonitor.hxx
@@ -22,31 +22,39 @@
#include "check.h"
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
#include <glib.h>
#endif
class EventLoop;
+/**
+ * This class monitors a timeout. Use Schedule() to begin the timeout
+ * or Cancel() to cancel it.
+ */
class TimeoutMonitor {
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
friend class EventLoop;
#endif
EventLoop &loop;
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
bool active;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
GSource *source;
#endif
public:
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
TimeoutMonitor(EventLoop &_loop)
:loop(_loop), active(false) {
}
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
TimeoutMonitor(EventLoop &_loop)
:loop(_loop), source(nullptr) {}
#endif
@@ -60,9 +68,11 @@ public:
}
bool IsActive() const {
-#ifdef USE_EPOLL
+#ifdef USE_INTERNAL_EVENTLOOP
return active;
-#else
+#endif
+
+#ifdef USE_GLIB_EVENTLOOP
return source != nullptr;
#endif
}
@@ -77,7 +87,7 @@ protected:
private:
void Run();
-#ifndef USE_EPOLL
+#ifdef USE_GLIB_EVENTLOOP
static gboolean Callback(gpointer data);
#endif
};