aboutsummaryrefslogtreecommitdiffstats
path: root/src/event
diff options
context:
space:
mode:
Diffstat (limited to 'src/event')
-rw-r--r--src/event/BufferedSocket.cxx5
-rw-r--r--src/event/BufferedSocket.hxx5
-rw-r--r--src/event/Call.cxx23
-rw-r--r--src/event/Call.hxx2
-rw-r--r--src/event/DeferredMonitor.cxx61
-rw-r--r--src/event/DeferredMonitor.hxx55
-rw-r--r--src/event/FullyBufferedSocket.cxx11
-rw-r--r--src/event/FullyBufferedSocket.hxx3
-rw-r--r--src/event/IdleMonitor.cxx34
-rw-r--r--src/event/IdleMonitor.hxx35
-rw-r--r--src/event/Loop.cxx203
-rw-r--r--src/event/Loop.hxx164
-rw-r--r--src/event/MultiSocketMonitor.cxx141
-rw-r--r--src/event/MultiSocketMonitor.hxx139
-rw-r--r--src/event/PollGroup.hxx41
-rw-r--r--src/event/PollGroupEPoll.hxx91
-rw-r--r--src/event/PollGroupPoll.cxx89
-rw-r--r--src/event/PollGroupPoll.hxx63
-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.cxx51
-rw-r--r--src/event/ServerSocket.hxx5
-rw-r--r--src/event/SignalMonitor.cxx11
-rw-r--r--src/event/SignalMonitor.hxx2
-rw-r--r--src/event/SocketMonitor.cxx101
-rw-r--r--src/event/SocketMonitor.hxx89
-rw-r--r--src/event/TimeoutMonitor.cxx33
-rw-r--r--src/event/TimeoutMonitor.hxx33
-rw-r--r--src/event/WakeFD.hxx2
30 files changed, 1004 insertions, 817 deletions
diff --git a/src/event/BufferedSocket.cxx b/src/event/BufferedSocket.cxx
index 92e350e85..c590c215d 100644
--- a/src/event/BufferedSocket.cxx
+++ b/src/event/BufferedSocket.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..1c9b44e46 100644
--- a/src/event/BufferedSocket.hxx
+++ b/src/event/BufferedSocket.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,13 +23,12 @@
#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..bc16c4e95 100644
--- a/src/event/Call.cxx
+++ b/src/event/Call.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -28,9 +28,7 @@
#include <assert.h>
class BlockingCallMonitor final
-#ifndef USE_EPOLL
: DeferredMonitor
-#endif
{
const std::function<void()> f;
@@ -40,24 +38,13 @@ class BlockingCallMonitor final
bool done;
public:
-#ifdef USE_EPOLL
- BlockingCallMonitor(EventLoop &loop, std::function<void()> &&_f)
- :f(std::move(_f)), done(false) {
- loop.AddCall([this](){
- this->DoRun();
- });
- }
-#else
BlockingCallMonitor(EventLoop &_loop, std::function<void()> &&_f)
:DeferredMonitor(_loop), f(std::move(_f)), done(false) {}
-#endif
void Run() {
-#ifndef USE_EPOLL
assert(!done);
Schedule();
-#endif
mutex.lock();
while (!done)
@@ -65,16 +52,8 @@ public:
mutex.unlock();
}
-#ifndef USE_EPOLL
private:
virtual void RunDeferred() override {
- DoRun();
- }
-
-#else
-public:
-#endif
- void DoRun() {
assert(!done);
f();
diff --git a/src/event/Call.hxx b/src/event/Call.hxx
index 34d886ca5..808965de1 100644
--- a/src/event/Call.hxx
+++ b/src/event/Call.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/event/DeferredMonitor.cxx b/src/event/DeferredMonitor.cxx
index 62edb7817..3e824012f 100644
--- a/src/event/DeferredMonitor.cxx
+++ b/src/event/DeferredMonitor.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,66 +24,11 @@
void
DeferredMonitor::Cancel()
{
-#ifdef USE_EPOLL
- pending = false;
-#else
- const ScopeLock protect(mutex);
- if (source_id != 0) {
- g_source_remove(source_id);
- source_id = 0;
- }
-#endif
+ loop.RemoveDeferred(*this);
}
void
DeferredMonitor::Schedule()
{
-#ifdef USE_EPOLL
- if (!pending.exchange(true))
- fd.Write();
-#else
- const ScopeLock protect(mutex);
- if (source_id == 0)
- source_id = loop.AddIdle(Callback, this);
-#endif
+ loop.AddDeferred(*this);
}
-
-#ifdef USE_EPOLL
-
-bool
-DeferredMonitor::OnSocketReady(unsigned)
-{
- fd.Read();
-
- if (pending.exchange(false))
- RunDeferred();
-
- return true;
-}
-
-#else
-
-void
-DeferredMonitor::Run()
-{
- {
- const ScopeLock protect(mutex);
- if (source_id == 0)
- /* cancelled */
- return;
-
- source_id = 0;
- }
-
- RunDeferred();
-}
-
-gboolean
-DeferredMonitor::Callback(gpointer data)
-{
- DeferredMonitor &monitor = *(DeferredMonitor *)data;
- monitor.Run();
- return false;
-}
-
-#endif
diff --git a/src/event/DeferredMonitor.hxx b/src/event/DeferredMonitor.hxx
index 2ac832a0a..3d3ab22b7 100644
--- a/src/event/DeferredMonitor.hxx
+++ b/src/event/DeferredMonitor.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -23,64 +23,31 @@
#include "check.h"
#include "Compiler.h"
-#ifdef USE_EPOLL
-#include "SocketMonitor.hxx"
-#include "WakeFD.hxx"
-#else
-#include "thread/Mutex.hxx"
-#include <glib.h>
-#endif
-
#include <atomic>
class EventLoop;
/**
* Defer execution of an event into an #EventLoop.
+ *
+ * This class is thread-safe.
*/
-class DeferredMonitor
-#ifdef USE_EPOLL
- : private SocketMonitor
-#endif
-{
-#ifdef USE_EPOLL
- std::atomic_bool pending;
- WakeFD fd;
-#else
+class DeferredMonitor {
EventLoop &loop;
- Mutex mutex;
-
- guint source_id;
-#endif
+ friend class EventLoop;
+ bool pending;
public:
-#ifdef USE_EPOLL
DeferredMonitor(EventLoop &_loop)
- :SocketMonitor(_loop), pending(false) {
- SocketMonitor::Open(fd.Get());
- SocketMonitor::Schedule(SocketMonitor::READ);
- }
-#else
- DeferredMonitor(EventLoop &_loop)
- :loop(_loop), source_id(0) {}
-#endif
+ :loop(_loop), pending(false) {}
~DeferredMonitor() {
-#ifdef USE_EPOLL
- /* avoid closing the WakeFD twice */
- SocketMonitor::Steal();
-#else
Cancel();
-#endif
}
EventLoop &GetEventLoop() {
-#ifdef USE_EPOLL
- return SocketMonitor::GetEventLoop();
-#else
return loop;
-#endif
}
void Schedule();
@@ -88,14 +55,6 @@ public:
protected:
virtual void RunDeferred() = 0;
-
-private:
-#ifdef USE_EPOLL
- virtual bool OnSocketReady(unsigned flags) override final;
-#else
- void Run();
- static gboolean Callback(gpointer data);
-#endif
};
#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/FullyBufferedSocket.cxx b/src/event/FullyBufferedSocket.cxx
index 8b57b1308..457add2b0 100644
--- a/src/event/FullyBufferedSocket.cxx
+++ b/src/event/FullyBufferedSocket.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,9 +20,9 @@
#include "config.h"
#include "FullyBufferedSocket.hxx"
#include "system/SocketError.hxx"
-#include "util/fifo_buffer.h"
#include "util/Error.hxx"
#include "util/Domain.hxx"
+#include "Compiler.h"
#include <assert.h>
#include <stdint.h>
@@ -59,15 +59,14 @@ FullyBufferedSocket::Flush()
{
assert(IsDefined());
- size_t length;
- const void *data = output.Read(&length);
- if (data == nullptr) {
+ const auto data = output.Read();
+ if (data.IsEmpty()) {
IdleMonitor::Cancel();
CancelWrite();
return true;
}
- auto nbytes = DirectWrite(data, length);
+ auto nbytes = DirectWrite(data.data, data.size);
if (gcc_unlikely(nbytes <= 0))
return nbytes == 0;
diff --git a/src/event/FullyBufferedSocket.hxx b/src/event/FullyBufferedSocket.hxx
index c50bb5f61..b03152be2 100644
--- a/src/event/FullyBufferedSocket.hxx
+++ b/src/event/FullyBufferedSocket.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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..4af656a22 100644
--- a/src/event/IdleMonitor.cxx
+++ b/src/event/IdleMonitor.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,38 +21,31 @@
#include "IdleMonitor.hxx"
#include "Loop.hxx"
+#include <assert.h>
+
void
IdleMonitor::Cancel()
{
- assert(loop.IsInside());
+ assert(loop.IsInsideOrNull());
if (!IsActive())
return;
-#ifdef USE_EPOLL
active = false;
loop.RemoveIdle(*this);
-#else
- g_source_remove(source_id);
- source_id = 0;
-#endif
}
void
IdleMonitor::Schedule()
{
- assert(loop.IsInside());
+ assert(loop.IsInsideOrVirgin());
if (IsActive())
/* already scheduled */
return;
-#ifdef USE_EPOLL
active = true;
loop.AddIdle(*this);
-#else
- source_id = loop.AddIdle(Callback, this);
-#endif
}
void
@@ -60,25 +53,8 @@ IdleMonitor::Run()
{
assert(loop.IsInside());
-#ifdef USE_EPOLL
assert(active);
active = false;
-#else
- assert(source_id != 0);
- source_id = 0;
-#endif
OnIdle();
}
-
-#ifndef USE_EPOLL
-
-gboolean
-IdleMonitor::Callback(gpointer data)
-{
- IdleMonitor &monitor = *(IdleMonitor *)data;
- monitor.Run();
- return false;
-}
-
-#endif
diff --git a/src/event/IdleMonitor.hxx b/src/event/IdleMonitor.hxx
index c8e79eb1d..65aaa38cf 100644
--- a/src/event/IdleMonitor.hxx
+++ b/src/event/IdleMonitor.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,41 +22,35 @@
#include "check.h"
-#ifndef USE_EPOLL
-#include <glib.h>
-#endif
-
class EventLoop;
/**
* An event that runs when the EventLoop has become idle, before
* waiting for more events. This class is not thread-safe; all
* methods must be run from EventLoop's thread.
+ *
+ * This class is not thread-safe, all methods must be called from the
+ * thread that runs the #EventLoop, except where explicitly documented
+ * as thread-safe.
*/
class IdleMonitor {
-#ifdef USE_EPOLL
friend class EventLoop;
-#endif
EventLoop &loop;
-#ifdef USE_EPOLL
bool active;
-#else
- guint source_id;
-#endif
public:
-#ifdef USE_EPOLL
IdleMonitor(EventLoop &_loop)
:loop(_loop), active(false) {}
-#else
- IdleMonitor(EventLoop &_loop)
- :loop(_loop), source_id(0) {}
-#endif
~IdleMonitor() {
- Cancel();
+#ifndef NDEBUG
+ /* this check is redundant, it is only here to avoid
+ the assertion in Cancel() */
+ if (IsActive())
+#endif
+ Cancel();
}
EventLoop &GetEventLoop() const {
@@ -64,11 +58,7 @@ public:
}
bool IsActive() const {
-#ifdef USE_EPOLL
return active;
-#else
- return source_id != 0;
-#endif
}
void Schedule();
@@ -79,9 +69,6 @@ protected:
private:
void Run();
-#ifndef USE_EPOLL
- static gboolean Callback(gpointer data);
-#endif
};
#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/Loop.cxx b/src/event/Loop.cxx
index 5aa24aea2..4ded68ff4 100644
--- a/src/event/Loop.cxx
+++ b/src/event/Loop.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,20 +20,21 @@
#include "config.h"
#include "Loop.hxx"
-#ifdef USE_EPOLL
-
#include "system/Clock.hxx"
#include "TimeoutMonitor.hxx"
#include "SocketMonitor.hxx"
#include "IdleMonitor.hxx"
+#include "DeferredMonitor.hxx"
#include <algorithm>
-EventLoop::EventLoop(Default)
+EventLoop::EventLoop()
:SocketMonitor(*this),
now_ms(::MonotonicClockMS()),
- quit(false),
- n_events(0),
+ quit(false), busy(true),
+#ifndef NDEBUG
+ virgin(true),
+#endif
thread(ThreadId::Null())
{
SocketMonitor::Open(wake_fd.Get());
@@ -45,45 +46,51 @@ EventLoop::~EventLoop()
assert(idle.empty());
assert(timers.empty());
- /* avoid closing the WakeFD twice */
- SocketMonitor::Steal();
+ /* this is necessary to get a well-defined destruction
+ order */
+ SocketMonitor::Cancel();
}
void
EventLoop::Break()
{
- if (IsInside())
- quit = true;
- else
- AddCall([this]() { Break(); });
+ quit = true;
+ wake_fd.Write();
}
-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;
+ assert(IsInsideOrVirgin());
+
+ poll_result.Clear(&m);
+ return poll_group.Abandon(_fd);
}
bool
EventLoop::RemoveFD(int _fd, SocketMonitor &m)
{
- Abandon(m);
- return epoll.Remove(_fd);
+ assert(IsInsideOrNull());
+
+ poll_result.Clear(&m);
+ return poll_group.Remove(_fd);
}
void
EventLoop::AddIdle(IdleMonitor &i)
{
+ assert(IsInsideOrVirgin());
assert(std::find(idle.begin(), idle.end(), &i) == idle.end());
idle.push_back(&i);
+ again = true;
}
void
EventLoop::RemoveIdle(IdleMonitor &i)
{
+ assert(IsInsideOrVirgin());
+
auto it = std::find(idle.begin(), idle.end(), &i);
assert(it != idle.end());
@@ -93,12 +100,19 @@ EventLoop::RemoveIdle(IdleMonitor &i)
void
EventLoop::AddTimer(TimeoutMonitor &t, unsigned ms)
{
+ /* can't use IsInsideOrVirgin() here because libavahi-client
+ modifies the timeout during avahi_client_free() */
+ assert(IsInsideOrNull());
+
timers.insert(TimerRecord(t, now_ms + ms));
+ again = true;
}
void
EventLoop::CancelTimer(TimeoutMonitor &t)
{
+ assert(IsInsideOrNull());
+
for (auto i = timers.begin(), end = timers.end(); i != end; ++i) {
if (&i->timer == &t) {
timers.erase(i);
@@ -107,19 +121,24 @@ EventLoop::CancelTimer(TimeoutMonitor &t)
}
}
-#endif
-
void
EventLoop::Run()
{
assert(thread.IsNull());
+ assert(virgin);
+
+#ifndef NDEBUG
+ virgin = false;
+#endif
+
thread = ThreadId::GetCurrent();
-#ifdef USE_EPOLL
assert(!quit);
+ assert(busy);
do {
now_ms = ::MonotonicClockMS();
+ again = false;
/* invoke timers */
@@ -146,7 +165,6 @@ EventLoop::Run()
/* invoke idle */
- const bool idle_empty = idle.empty();
while (!idle.empty()) {
IdleMonitor &m = *idle.front();
idle.pop_front();
@@ -156,7 +174,14 @@ EventLoop::Run()
return;
}
- if (!idle_empty)
+ /* try to handle DeferredMonitors without WakeFD
+ overhead */
+ mutex.lock();
+ HandleDeferred();
+ busy = false;
+ mutex.unlock();
+
+ if (again)
/* re-evaluate timers because one of the
IdleMonitors may have added a new
timeout */
@@ -164,101 +189,107 @@ 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);
+ mutex.lock();
+ busy = true;
+ mutex.unlock();
/* 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) {
if (quit)
break;
+
+ auto m = (SocketMonitor *)poll_result.GetObject(i);
+ m->Dispatch(events);
}
}
- n_events = 0;
+ poll_result.Reset();
+
} while (!quit);
-#else
- g_main_loop_run(loop);
-#endif
+#ifndef NDEBUG
+ assert(busy);
assert(thread.IsInside());
+ thread = ThreadId::Null();
+#endif
}
-#ifdef USE_EPOLL
-
void
-EventLoop::AddCall(std::function<void()> &&f)
+EventLoop::AddDeferred(DeferredMonitor &d)
{
mutex.lock();
- calls.push_back(f);
+ if (d.pending) {
+ mutex.unlock();
+ return;
+ }
+
+ assert(std::find(deferred.begin(),
+ deferred.end(), &d) == deferred.end());
+
+ /* we don't need to wake up the EventLoop if another
+ DeferredMonitor has already done it */
+ const bool must_wake = !busy && deferred.empty();
+
+ d.pending = true;
+ deferred.push_back(&d);
+ again = true;
mutex.unlock();
- wake_fd.Write();
+ if (must_wake)
+ wake_fd.Write();
}
-bool
-EventLoop::OnSocketReady(gcc_unused unsigned flags)
+void
+EventLoop::RemoveDeferred(DeferredMonitor &d)
{
- assert(!quit);
+ const ScopeLock protect(mutex);
- wake_fd.Read();
+ if (!d.pending) {
+ assert(std::find(deferred.begin(),
+ deferred.end(), &d) == deferred.end());
+ return;
+ }
- mutex.lock();
+ d.pending = false;
- while (!calls.empty() && !quit) {
- auto f = std::move(calls.front());
- calls.pop_front();
+ auto i = std::find(deferred.begin(), deferred.end(), &d);
+ assert(i != deferred.end());
+
+ deferred.erase(i);
+}
+
+void
+EventLoop::HandleDeferred()
+{
+ while (!deferred.empty() && !quit) {
+ DeferredMonitor &m = *deferred.front();
+ assert(m.pending);
+
+ deferred.pop_front();
+ m.pending = false;
mutex.unlock();
- f();
+ m.RunDeferred();
mutex.lock();
}
-
- mutex.unlock();
-
- return true;
}
-#else
-
-guint
-EventLoop::AddIdle(GSourceFunc function, gpointer data)
+bool
+EventLoop::OnSocketReady(gcc_unused unsigned flags)
{
- GSource *source = g_idle_source_new();
- g_source_set_callback(source, function, data, nullptr);
- guint id = g_source_attach(source, GetContext());
- g_source_unref(source);
- return id;
-}
+ assert(IsInside());
-GSource *
-EventLoop::AddTimeout(guint interval_ms,
- GSourceFunc function, gpointer data)
-{
- GSource *source = g_timeout_source_new(interval_ms);
- g_source_set_callback(source, function, data, nullptr);
- g_source_attach(source, GetContext());
- return source;
-}
+ wake_fd.Read();
-GSource *
-EventLoop::AddTimeoutSeconds(guint interval_s,
- GSourceFunc function, gpointer data)
-{
- GSource *source = g_timeout_source_new_seconds(interval_s);
- g_source_set_callback(source, function, data, nullptr);
- g_source_attach(source, GetContext());
- return source;
-}
+ mutex.lock();
+ HandleDeferred();
+ mutex.unlock();
-#endif
+ return true;
+}
diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx
index 62e733747..56804dc81 100644
--- a/src/event/Loop.hxx
+++ b/src/event/Loop.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -24,33 +24,32 @@
#include "thread/Id.hxx"
#include "Compiler.h"
-#ifdef USE_EPOLL
-#include "system/EPollFD.hxx"
+#include "PollGroup.hxx"
#include "thread/Mutex.hxx"
#include "WakeFD.hxx"
#include "SocketMonitor.hxx"
-#include <functional>
#include <list>
#include <set>
-#else
-#include <glib.h>
-#endif
-#ifdef USE_EPOLL
class TimeoutMonitor;
class IdleMonitor;
+class DeferredMonitor;
class SocketMonitor;
-#endif
#include <assert.h>
-class EventLoop final
-#ifdef USE_EPOLL
- : private SocketMonitor
-#endif
+/**
+ * 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 : SocketMonitor
{
-#ifdef USE_EPOLL
struct TimerRecord {
/**
* Projected monotonic_clock_ms() value when this
@@ -73,52 +72,78 @@ class EventLoop final
}
};
- EPollFD epoll;
-
WakeFD wake_fd;
std::multiset<TimerRecord> timers;
std::list<IdleMonitor *> idle;
Mutex mutex;
- std::list<std::function<void()>> calls;
+ std::list<DeferredMonitor *> deferred;
unsigned now_ms;
bool quit;
- static constexpr unsigned MAX_EVENTS = 16;
- unsigned n_events;
- epoll_event events[MAX_EVENTS];
-#else
- GMainContext *context;
- GMainLoop *loop;
+ /**
+ * True when the object has been modified and another check is
+ * necessary before going to sleep via PollGroup::ReadEvents().
+ */
+ bool again;
+
+ /**
+ * True when handling callbacks, false when waiting for I/O or
+ * timeout.
+ *
+ * Protected with #mutex.
+ */
+ bool busy;
+
+#ifndef NDEBUG
+ /**
+ * True if Run() was never called. This is used for assert()
+ * calls.
+ */
+ bool virgin;
#endif
+ PollGroup poll_group;
+ PollResult poll_result;
+
/**
* A reference to the thread that is currently inside Run().
*/
ThreadId thread;
public:
-#ifdef USE_EPOLL
- struct Default {};
-
- EventLoop(Default dummy=Default());
+ EventLoop();
~EventLoop();
+ /**
+ * A caching wrapper for MonotonicClockMS().
+ */
unsigned GetTimeMS() const {
+ assert(IsInside());
+
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);
+ assert(thread.IsNull() || thread.IsInside());
+
+ return poll_group.Add(_fd, flags, &m);
}
bool ModifyFD(int _fd, unsigned flags, SocketMonitor &m) {
- return epoll.Modify(_fd, flags, &m);
+ assert(IsInside());
+
+ return poll_group.Modify(_fd, flags, &m);
}
/**
@@ -126,7 +151,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);
@@ -136,53 +161,38 @@ public:
void AddTimer(TimeoutMonitor &t, unsigned ms);
void CancelTimer(TimeoutMonitor &t);
- void AddCall(std::function<void()> &&f);
+ /**
+ * Schedule a call to DeferredMonitor::RunDeferred().
+ *
+ * This method is thread-safe.
+ */
+ void AddDeferred(DeferredMonitor &d);
+ /**
+ * Cancel a pending call to DeferredMonitor::RunDeferred().
+ * However after returning, the call may still be running.
+ *
+ * This method is thread-safe.
+ */
+ void RemoveDeferred(DeferredMonitor &d);
+
+ /**
+ * The main function of this class. It will loop until
+ * Break() gets called. Can be called only once.
+ */
void Run();
private:
+ /**
+ * Invoke all pending DeferredMonitors.
+ *
+ * Caller must lock the mutex.
+ */
+ void HandleDeferred();
+
virtual bool OnSocketReady(unsigned flags) override;
public:
-#else
- EventLoop()
- :context(g_main_context_new()),
- loop(g_main_loop_new(context, false)),
- thread(ThreadId::Null()) {}
-
- struct Default {};
- EventLoop(gcc_unused Default _dummy)
- :context(g_main_context_ref(g_main_context_default())),
- loop(g_main_loop_new(context, false)),
- thread(ThreadId::Null()) {}
-
- ~EventLoop() {
- g_main_loop_unref(loop);
- g_main_context_unref(context);
- }
-
- GMainContext *GetContext() {
- return context;
- }
-
- void WakeUp() {
- g_main_context_wakeup(context);
- }
-
- void Break() {
- g_main_loop_quit(loop);
- }
-
- void Run();
-
- guint AddIdle(GSourceFunc function, gpointer data);
-
- GSource *AddTimeout(guint interval_ms,
- GSourceFunc function, gpointer data);
-
- GSource *AddTimeoutSeconds(guint interval_s,
- GSourceFunc function, gpointer data);
-#endif
/**
* Are we currently running inside this EventLoop's thread?
@@ -193,6 +203,20 @@ public:
return thread.IsInside();
}
+
+#ifndef NDEBUG
+ gcc_pure
+ bool IsInsideOrVirgin() const {
+ return virgin || IsInside();
+ }
+#endif
+
+#ifndef NDEBUG
+ gcc_pure
+ bool IsInsideOrNull() const {
+ return thread.IsNull() || thread.IsInside();
+ }
+#endif
};
#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/MultiSocketMonitor.cxx b/src/event/MultiSocketMonitor.cxx
index bd1aa6fef..ef77de425 100644
--- a/src/event/MultiSocketMonitor.cxx
+++ b/src/event/MultiSocketMonitor.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,12 +20,12 @@
#include "config.h"
#include "MultiSocketMonitor.hxx"
#include "Loop.hxx"
-#include "system/fd_util.h"
-#include "Compiler.h"
-#include <assert.h>
+#include <algorithm>
-#ifdef USE_EPOLL
+#ifndef WIN32
+#include <poll.h>
+#endif
MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop)
:IdleMonitor(_loop), TimeoutMonitor(_loop), ready(false) {
@@ -37,6 +37,40 @@ MultiSocketMonitor::~MultiSocketMonitor()
}
void
+MultiSocketMonitor::ClearSocketList()
+{
+ assert(GetEventLoop().IsInsideOrNull());
+
+ fds.clear();
+}
+
+#ifndef WIN32
+
+void
+MultiSocketMonitor::ReplaceSocketList(pollfd *pfds, unsigned n)
+{
+ pollfd *const end = pfds + n;
+
+ UpdateSocketList([pfds, end](int fd) -> unsigned {
+ auto i = std::find_if(pfds, end, [fd](const struct pollfd &pfd){
+ return pfd.fd == fd;
+ });
+ if (i == end)
+ return 0;
+
+ auto events = i->events;
+ i->events = 0;
+ return events;
+ });
+
+ for (auto i = pfds; i != end; ++i)
+ if (i->events != 0)
+ AddSocket(i->fd, i->events);
+}
+
+#endif
+
+void
MultiSocketMonitor::Prepare()
{
int timeout_ms = PrepareSockets();
@@ -64,100 +98,3 @@ MultiSocketMonitor::OnIdle()
Prepare();
}
}
-
-#else
-
-/**
- * The vtable for our GSource implementation. Unfortunately, we
- * cannot declare it "const", because g_source_new() takes a non-const
- * pointer, for whatever reason.
- */
-static GSourceFuncs multi_socket_monitor_source_funcs = {
- MultiSocketMonitor::Prepare,
- MultiSocketMonitor::Check,
- MultiSocketMonitor::Dispatch,
- nullptr,
- nullptr,
- nullptr,
-};
-
-MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop)
- :loop(_loop),
- source((Source *)g_source_new(&multi_socket_monitor_source_funcs,
- sizeof(*source))),
- absolute_timeout_us(-1) {
- source->monitor = this;
-
- g_source_attach(&source->base, loop.GetContext());
-}
-
-MultiSocketMonitor::~MultiSocketMonitor()
-{
- g_source_destroy(&source->base);
- g_source_unref(&source->base);
- source = nullptr;
-}
-
-bool
-MultiSocketMonitor::Prepare(gint *timeout_r)
-{
- int timeout_ms = *timeout_r = PrepareSockets();
- absolute_timeout_us = timeout_ms < 0
- ? uint64_t(-1)
- : GetTime() + uint64_t(timeout_ms) * 1000;
-
- return false;
-}
-
-bool
-MultiSocketMonitor::Check() const
-{
- if (GetTime() >= absolute_timeout_us)
- return true;
-
- for (const auto &i : fds)
- if (i.GetReturnedEvents() != 0)
- return true;
-
- return false;
-}
-
-/*
- * GSource methods
- *
- */
-
-gboolean
-MultiSocketMonitor::Prepare(GSource *_source, gint *timeout_r)
-{
- Source &source = *(Source *)_source;
- MultiSocketMonitor &monitor = *source.monitor;
- assert(_source == &monitor.source->base);
-
- return monitor.Prepare(timeout_r);
-}
-
-gboolean
-MultiSocketMonitor::Check(GSource *_source)
-{
- const Source &source = *(const Source *)_source;
- const MultiSocketMonitor &monitor = *source.monitor;
- assert(_source == &monitor.source->base);
-
- return monitor.Check();
-}
-
-gboolean
-MultiSocketMonitor::Dispatch(GSource *_source,
- gcc_unused GSourceFunc callback,
- gcc_unused gpointer user_data)
-{
- Source &source = *(Source *)_source;
- MultiSocketMonitor &monitor = *source.monitor;
- assert(_source == &monitor.source->base);
-
- monitor.Dispatch();
- return true;
-}
-
-#endif
diff --git a/src/event/MultiSocketMonitor.hxx b/src/event/MultiSocketMonitor.hxx
index 8ee81a508..b40ee8caa 100644
--- a/src/event/MultiSocketMonitor.hxx
+++ b/src/event/MultiSocketMonitor.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,40 +21,38 @@
#define MPD_MULTI_SOCKET_MONITOR_HXX
#include "check.h"
-#include "Compiler.h"
-
-#ifdef USE_EPOLL
#include "IdleMonitor.hxx"
#include "TimeoutMonitor.hxx"
#include "SocketMonitor.hxx"
-#else
-#include <glib.h>
-#endif
+#include "Compiler.h"
#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
#endif
+#ifndef WIN32
+struct pollfd;
+#endif
+
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
- : private IdleMonitor, private TimeoutMonitor
-#endif
+class MultiSocketMonitor : IdleMonitor, TimeoutMonitor
{
-#ifdef USE_EPOLL
class SingleFD final : public SocketMonitor {
MultiSocketMonitor &multi;
@@ -99,93 +97,45 @@ class MultiSocketMonitor
friend class SingleFD;
bool ready, refresh;
-#else
- struct Source {
- GSource base;
-
- MultiSocketMonitor *monitor;
- };
-
- struct SingleFD {
- GPollFD pfd;
-
- constexpr SingleFD(gcc_unused MultiSocketMonitor &m,
- int fd, unsigned events)
- :pfd{fd, gushort(events), 0} {}
-
- constexpr int GetFD() const {
- return pfd.fd;
- }
-
- constexpr unsigned GetEvents() const {
- return pfd.events;
- }
-
- constexpr unsigned GetReturnedEvents() const {
- return pfd.revents;
- }
-
- void SetEvents(unsigned _events) {
- pfd.events = _events;
- }
- };
-
- EventLoop &loop;
- Source *source;
- uint64_t absolute_timeout_us;
-#endif
std::forward_list<SingleFD> fds;
public:
-#ifdef USE_EPOLL
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
- 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
MultiSocketMonitor(EventLoop &_loop);
~MultiSocketMonitor();
-#ifdef USE_EPOLL
using IdleMonitor::GetEventLoop;
-#else
- EventLoop &GetEventLoop() {
- return loop;
- }
-#endif
public:
-#ifndef USE_EPOLL
- gcc_pure
- uint64_t GetTime() const {
- return g_source_get_time(&source->base);
- }
-#endif
-
+ /**
+ * Invalidate the socket list. A call to PrepareSockets() is
+ * scheduled which will then update the list.
+ */
void InvalidateSockets() {
-#ifdef USE_EPOLL
refresh = true;
IdleMonitor::Schedule();
-#else
- /* no-op because GLib always calls the GSource's
- "prepare" method before each poll() anyway */
-#endif
}
void AddSocket(int fd, unsigned events) {
fds.emplace_front(*this, fd, events);
-#ifndef USE_EPOLL
- g_source_add_poll(&source->base, &fds.front().pfd);
-#endif
}
+ /**
+ * Remove all sockets.
+ */
+ void ClearSocketList();
+
+ /**
+ * Update the known sockets by invoking the given function for
+ * each one; its return value is the events bit mask. A
+ * return value of 0 means the socket will be removed from the
+ * list.
+ */
template<typename E>
void UpdateSocketList(E &&e) {
for (auto prev = fds.before_begin(), end = fds.end(),
@@ -198,16 +148,19 @@ public:
i->SetEvents(events);
prev = i;
} else {
-#ifdef USE_EPOLL
- i->Steal();
-#else
- g_source_remove_poll(&source->base, &i->pfd);
-#endif
fds.erase_after(prev);
}
}
}
+#ifndef WIN32
+ /**
+ * Replace the socket list with the given file descriptors.
+ * The given pollfd array will be modified by this method.
+ */
+ void ReplaceSocketList(pollfd *pfds, unsigned n);
+#endif
+
protected:
/**
* @return timeout [ms] or -1 for no timeout
@@ -215,7 +168,6 @@ protected:
virtual int PrepareSockets() = 0;
virtual void DispatchSockets() = 0;
-#ifdef USE_EPOLL
private:
void SetReady() {
ready = true;
@@ -230,23 +182,6 @@ private:
}
virtual void OnIdle() final;
-
-#else
-public:
- /* GSource callbacks */
- static gboolean Prepare(GSource *source, gint *timeout_r);
- static gboolean Check(GSource *source);
- static gboolean Dispatch(GSource *source, GSourceFunc callback,
- gpointer user_data);
-
-private:
- bool Prepare(gint *timeout_r);
- bool Check() const;
-
- void Dispatch() {
- DispatchSockets();
- }
-#endif
};
#endif
diff --git a/src/event/PollGroup.hxx b/src/event/PollGroup.hxx
new file mode 100644
index 000000000..a2f176860
--- /dev/null
+++ b/src/event/PollGroup.hxx
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2003-2014 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..d8edb8a1f
--- /dev/null
+++ b/src/event/PollGroupEPoll.hxx
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2003-2014 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..402f8616f
--- /dev/null
+++ b/src/event/PollGroupPoll.cxx
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2003-2014 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());
+
+ const size_t index = poll_events.size();
+ poll_events.resize(index + 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..f7a3ccb4f
--- /dev/null
+++ b/src/event/PollGroupPoll.hxx
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2003-2014 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 <vector>
+#include <unordered_map>
+
+#include <stddef.h>
+#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..26c8abd46
--- /dev/null
+++ b/src/event/PollGroupWinSelect.cxx
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2003-2014 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..d01067709
--- /dev/null
+++ b/src/event/PollGroupWinSelect.hxx
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2003-2014 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..35daf7f08
--- /dev/null
+++ b/src/event/PollResultGeneric.hxx
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2003-2014 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..ce70a969b 100644
--- a/src/event/ServerSocket.cxx
+++ b/src/event/ServerSocket.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -18,11 +18,6 @@
*/
#include "config.h"
-
-#ifdef HAVE_STRUCT_UCRED
-#define _GNU_SOURCE 1
-#endif
-
#include "ServerSocket.hxx"
#include "system/SocketUtil.hxx"
#include "system/SocketError.hxx"
@@ -31,13 +26,13 @@
#include "system/fd_util.h"
#include "fs/AllocatedPath.hxx"
#include "fs/FileSystem.hxx"
+#include "util/Alloc.hxx"
#include "util/Error.hxx"
#include "util/Domain.hxx"
#include "Log.hxx"
-#include <glib.h>
-
#include <string>
+#include <algorithm>
#include <sys/types.h>
#include <sys/stat.h>
@@ -78,7 +73,7 @@ public:
parent(_parent), serial(_serial),
path(AllocatedPath::Null()),
address_length(_address_length),
- address((sockaddr *)g_memdup(_address, _address_length))
+ address((sockaddr *)xmemdup(_address, _address_length))
{
assert(_address != nullptr);
assert(_address_length > 0);
@@ -88,7 +83,10 @@ public:
OneServerSocket &operator=(const OneServerSocket &other) = delete;
~OneServerSocket() {
- g_free(address);
+ free(address);
+
+ if (IsDefined())
+ Close();
}
unsigned GetSerial() const {
@@ -106,7 +104,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 +122,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 +231,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..4c3fd9f1d 100644
--- a/src/event/ServerSocket.hxx
+++ b/src/event/ServerSocket.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -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 4f5174377..2d8fe681f 100644
--- a/src/event/SignalMonitor.cxx
+++ b/src/event/SignalMonitor.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -43,6 +43,7 @@
#include <pthread.h>
#endif
+#include <assert.h>
#include <signal.h>
class SignalMonitor final : private SocketMonitor {
@@ -61,14 +62,6 @@ public:
#endif
}
- ~SignalMonitor() {
- /* prevent the descriptor to be closed twice */
-#ifdef USE_SIGNALFD
- if (SocketMonitor::IsDefined())
-#endif
- SocketMonitor::Steal();
- }
-
using SocketMonitor::GetEventLoop;
#ifdef USE_SIGNALFD
diff --git a/src/event/SignalMonitor.hxx b/src/event/SignalMonitor.hxx
index 1ecccd40b..a41e57ef9 100644
--- a/src/event/SignalMonitor.hxx
+++ b/src/event/SignalMonitor.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
diff --git a/src/event/SocketMonitor.cxx b/src/event/SocketMonitor.cxx
index 2b97059f7..69207287d 100644
--- a/src/event/SocketMonitor.cxx
+++ b/src/event/SocketMonitor.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -28,12 +28,9 @@
#ifdef WIN32
#include <winsock2.h>
#else
-#include <sys/types.h>
#include <sys/socket.h>
#endif
-#ifdef USE_EPOLL
-
void
SocketMonitor::Dispatch(unsigned flags)
{
@@ -43,93 +40,19 @@ SocketMonitor::Dispatch(unsigned flags)
Cancel();
}
-#else
-
-/*
- * GSource methods
- *
- */
-
-gboolean
-SocketMonitor::Prepare(gcc_unused GSource *source, gcc_unused gint *timeout_r)
-{
- return false;
-}
-
-gboolean
-SocketMonitor::Check(GSource *_source)
-{
- const Source &source = *(const Source *)_source;
- const SocketMonitor &monitor = *source.monitor;
- assert(_source == &monitor.source->base);
-
- return monitor.Check();
-}
-
-gboolean
-SocketMonitor::Dispatch(GSource *_source,
- gcc_unused GSourceFunc callback,
- gcc_unused gpointer user_data)
-{
- Source &source = *(Source *)_source;
- SocketMonitor &monitor = *source.monitor;
- assert(_source == &monitor.source->base);
-
- monitor.Dispatch();
- return true;
-}
-
-/**
- * The vtable for our GSource implementation. Unfortunately, we
- * cannot declare it "const", because g_source_new() takes a non-const
- * pointer, for whatever reason.
- */
-static GSourceFuncs socket_monitor_source_funcs = {
- SocketMonitor::Prepare,
- SocketMonitor::Check,
- SocketMonitor::Dispatch,
- nullptr,
- nullptr,
- nullptr,
-};
-
-SocketMonitor::SocketMonitor(int _fd, EventLoop &_loop)
- :fd(-1), loop(_loop),
- source(nullptr) {
- assert(_fd >= 0);
-
- Open(_fd);
-}
-
-#endif
-
SocketMonitor::~SocketMonitor()
{
if (IsDefined())
- Close();
+ Cancel();
}
void
SocketMonitor::Open(int _fd)
{
assert(fd < 0);
-#ifndef USE_EPOLL
- assert(source == nullptr);
-#endif
assert(_fd >= 0);
fd = _fd;
-
-#ifndef USE_EPOLL
- poll = {fd, 0, 0};
-
- source = (Source *)g_source_new(&socket_monitor_source_funcs,
- sizeof(*source));
- source->monitor = this;
-
- g_source_attach(&source->base, loop.GetContext());
- g_source_add_poll(&source->base, &poll);
-#endif
}
int
@@ -142,12 +65,6 @@ SocketMonitor::Steal()
int result = fd;
fd = -1;
-#ifndef USE_EPOLL
- g_source_destroy(&source->base);
- g_source_unref(&source->base);
- source = nullptr;
-#endif
-
return result;
}
@@ -156,12 +73,9 @@ SocketMonitor::Abandon()
{
assert(IsDefined());
-#ifdef USE_EPOLL
+ int old_fd = fd;
fd = -1;
- loop.Abandon(*this);
-#else
- Steal();
-#endif
+ loop.Abandon(old_fd, *this);
}
void
@@ -178,7 +92,6 @@ SocketMonitor::Schedule(unsigned flags)
if (flags == GetScheduledFlags())
return;
-#ifdef USE_EPOLL
if (scheduled_flags == 0)
loop.AddFD(fd, flags, *this);
else if (flags == 0)
@@ -187,12 +100,6 @@ SocketMonitor::Schedule(unsigned flags)
loop.ModifyFD(fd, flags, *this);
scheduled_flags = flags;
-#else
- poll.events = flags;
- poll.revents &= flags;
-
- loop.WakeUp();
-#endif
}
SocketMonitor::ssize_t
diff --git a/src/event/SocketMonitor.hxx b/src/event/SocketMonitor.hxx
index 5369ddb8a..56d4273f0 100644
--- a/src/event/SocketMonitor.hxx
+++ b/src/event/SocketMonitor.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -21,12 +21,7 @@
#define MPD_SOCKET_MONITOR_HXX
#include "check.h"
-
-#ifdef USE_EPOLL
-#include <sys/epoll.h>
-#else
-#include <glib.h>
-#endif
+#include "PollGroup.hxx"
#include <type_traits>
@@ -34,8 +29,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,56 +38,41 @@
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.
+ *
+ * This class does not feel responsible for closing the socket. Call
+ * Close() to do it manually.
+ *
+ * This class is not thread-safe, all methods must be called from the
+ * thread that runs the #EventLoop, except where explicitly documented
+ * as thread-safe.
+ */
class SocketMonitor {
-#ifdef USE_EPOLL
-#else
- struct Source {
- GSource base;
-
- SocketMonitor *monitor;
- };
-#endif
-
int fd;
EventLoop &loop;
-#ifdef USE_EPOLL
/**
* A bit mask of events that is currently registered in the EventLoop.
*/
unsigned scheduled_flags;
-#else
- 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
- 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
+ static constexpr unsigned READ = PollGroup::READ;
+ static constexpr unsigned WRITE = PollGroup::WRITE;
+ static constexpr unsigned ERROR = PollGroup::ERROR;
+ static constexpr unsigned HANGUP = PollGroup::HANGUP;
typedef std::make_signed<size_t>::type ssize_t;
-#ifdef USE_EPOLL
SocketMonitor(EventLoop &_loop)
:fd(-1), loop(_loop), scheduled_flags(0) {}
SocketMonitor(int _fd, EventLoop &_loop)
:fd(_fd), loop(_loop), scheduled_flags(0) {}
-#else
- SocketMonitor(EventLoop &_loop)
- :fd(-1), loop(_loop), source(nullptr) {}
-
- SocketMonitor(int _fd, EventLoop &_loop);
-#endif
~SocketMonitor();
@@ -114,7 +94,7 @@ public:
/**
* "Steal" the socket descriptor. This abandons the socket
- * and puts the responsibility for closing it to the caller.
+ * and returns it.
*/
int Steal();
@@ -128,11 +108,7 @@ public:
unsigned GetScheduledFlags() const {
assert(IsDefined());
-#ifdef USE_EPOLL
return scheduled_flags;
-#else
- return poll.events;
-#endif
}
void Schedule(unsigned flags);
@@ -167,28 +143,7 @@ protected:
virtual bool OnSocketReady(unsigned flags) = 0;
public:
-#ifdef USE_EPOLL
void Dispatch(unsigned flags);
-#else
- /* GSource callbacks */
- static gboolean Prepare(GSource *source, gint *timeout_r);
- static gboolean Check(GSource *source);
- static gboolean Dispatch(GSource *source, GSourceFunc callback,
- gpointer user_data);
-
-private:
- bool Check() const {
- assert(IsDefined());
-
- return (poll.revents & poll.events) != 0;
- }
-
- void Dispatch() {
- assert(IsDefined());
-
- OnSocketReady(poll.revents & poll.events);
- }
-#endif
};
#endif
diff --git a/src/event/TimeoutMonitor.cxx b/src/event/TimeoutMonitor.cxx
index cffad6b92..e04af3e4e 100644
--- a/src/event/TimeoutMonitor.cxx
+++ b/src/event/TimeoutMonitor.cxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -25,28 +25,19 @@ void
TimeoutMonitor::Cancel()
{
if (IsActive()) {
-#ifdef USE_EPOLL
active = false;
loop.CancelTimer(*this);
-#else
- g_source_destroy(source);
- g_source_unref(source);
- source = nullptr;
-#endif
}
}
void
+
TimeoutMonitor::Schedule(unsigned ms)
{
Cancel();
-#ifdef USE_EPOLL
active = true;
loop.AddTimer(*this, ms);
-#else
- source = loop.AddTimeout(ms, Callback, this);
-#endif
}
void
@@ -54,31 +45,11 @@ TimeoutMonitor::ScheduleSeconds(unsigned s)
{
Cancel();
-#ifdef USE_EPOLL
Schedule(s * 1000u);
-#else
- source = loop.AddTimeoutSeconds(s, Callback, this);
-#endif
}
void
TimeoutMonitor::Run()
{
-#ifndef USE_EPOLL
- Cancel();
-#endif
-
OnTimeout();
}
-
-#ifndef USE_EPOLL
-
-gboolean
-TimeoutMonitor::Callback(gpointer data)
-{
- TimeoutMonitor &monitor = *(TimeoutMonitor *)data;
- monitor.Run();
- return false;
-}
-
-#endif
diff --git a/src/event/TimeoutMonitor.hxx b/src/event/TimeoutMonitor.hxx
index 98e4e5564..414d48aa6 100644
--- a/src/event/TimeoutMonitor.hxx
+++ b/src/event/TimeoutMonitor.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -22,34 +22,27 @@
#include "check.h"
-#ifndef USE_EPOLL
-#include <glib.h>
-#endif
-
class EventLoop;
+/**
+ * This class monitors a timeout. Use Schedule() to begin the timeout
+ * or Cancel() to cancel it.
+ *
+ * This class is not thread-safe, all methods must be called from the
+ * thread that runs the #EventLoop, except where explicitly documented
+ * as thread-safe.
+ */
class TimeoutMonitor {
-#ifdef USE_EPOLL
friend class EventLoop;
-#endif
EventLoop &loop;
-#ifdef USE_EPOLL
bool active;
-#else
- GSource *source;
-#endif
public:
-#ifdef USE_EPOLL
TimeoutMonitor(EventLoop &_loop)
:loop(_loop), active(false) {
}
-#else
- TimeoutMonitor(EventLoop &_loop)
- :loop(_loop), source(nullptr) {}
-#endif
~TimeoutMonitor() {
Cancel();
@@ -60,11 +53,7 @@ public:
}
bool IsActive() const {
-#ifdef USE_EPOLL
return active;
-#else
- return source != nullptr;
-#endif
}
void Schedule(unsigned ms);
@@ -76,10 +65,6 @@ protected:
private:
void Run();
-
-#ifndef USE_EPOLL
- static gboolean Callback(gpointer data);
-#endif
};
#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/WakeFD.hxx b/src/event/WakeFD.hxx
index ed1baafd8..c6222b59c 100644
--- a/src/event/WakeFD.hxx
+++ b/src/event/WakeFD.hxx
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2013 The Music Player Daemon Project
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify