aboutsummaryrefslogtreecommitdiffstats
path: root/src/event
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/event/BufferedSocket.cxx155
-rw-r--r--src/event/BufferedSocket.hxx105
-rw-r--r--src/event/Call.cxx102
-rw-r--r--src/event/Call.hxx36
-rw-r--r--src/event/DeferredMonitor.cxx81
-rw-r--r--src/event/DeferredMonitor.hxx98
-rw-r--r--src/event/FullyBufferedSocket.cxx136
-rw-r--r--src/event/FullyBufferedSocket.hxx63
-rw-r--r--src/event/IdleMonitor.cxx84
-rw-r--r--src/event/IdleMonitor.hxx87
-rw-r--r--src/event/Loop.cxx259
-rw-r--r--src/event/Loop.hxx191
-rw-r--r--src/event/MultiSocketMonitor.cxx163
-rw-r--r--src/event/MultiSocketMonitor.hxx254
-rw-r--r--src/event/ServerSocket.cxx434
-rw-r--r--src/event/ServerSocket.hxx120
-rw-r--r--src/event/SignalMonitor.cxx204
-rw-r--r--src/event/SignalMonitor.hxx64
-rw-r--r--src/event/SocketMonitor.cxx212
-rw-r--r--src/event/SocketMonitor.hxx189
-rw-r--r--src/event/TimeoutMonitor.cxx84
-rw-r--r--src/event/TimeoutMonitor.hxx85
-rw-r--r--src/event/WakeFD.hxx35
-rw-r--r--src/event_pipe.c164
-rw-r--r--src/event_pipe.h71
25 files changed, 3241 insertions, 235 deletions
diff --git a/src/event/BufferedSocket.cxx b/src/event/BufferedSocket.cxx
new file mode 100644
index 000000000..f333a5987
--- /dev/null
+++ b/src/event/BufferedSocket.cxx
@@ -0,0 +1,155 @@
+/*
+ * 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"
+#include "BufferedSocket.hxx"
+#include "system/SocketError.hxx"
+#include "util/fifo_buffer.h"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+BufferedSocket::~BufferedSocket()
+{
+ if (input != nullptr)
+ fifo_buffer_free(input);
+}
+
+BufferedSocket::ssize_t
+BufferedSocket::DirectRead(void *data, size_t length)
+{
+ const auto nbytes = SocketMonitor::Read((char *)data, length);
+ if (gcc_likely(nbytes > 0))
+ return nbytes;
+
+ if (nbytes == 0) {
+ OnSocketClosed();
+ return -1;
+ }
+
+ const auto code = GetSocketError();
+ if (IsSocketErrorAgain(code))
+ return 0;
+
+ if (IsSocketErrorClosed(code))
+ OnSocketClosed();
+ else
+ OnSocketError(NewSocketError(code));
+ return -1;
+}
+
+bool
+BufferedSocket::ReadToBuffer()
+{
+ assert(IsDefined());
+
+ if (input == nullptr)
+ input = fifo_buffer_new(8192);
+
+ size_t length;
+ void *buffer = fifo_buffer_write(input, &length);
+ assert(buffer != nullptr);
+
+ const auto nbytes = DirectRead(buffer, length);
+ if (nbytes > 0)
+ fifo_buffer_append(input, nbytes);
+
+ return nbytes >= 0;
+}
+
+bool
+BufferedSocket::ResumeInput()
+{
+ assert(IsDefined());
+
+ if (input == nullptr) {
+ ScheduleRead();
+ return true;
+ }
+
+ while (true) {
+ size_t length;
+ const void *data = fifo_buffer_read(input, &length);
+ if (data == nullptr) {
+ ScheduleRead();
+ return true;
+ }
+
+ const auto result = OnSocketInput(data, length);
+ switch (result) {
+ case InputResult::MORE:
+ if (fifo_buffer_is_full(input)) {
+ // TODO
+ static constexpr Domain buffered_socket_domain("buffered_socket");
+ Error error;
+ error.Set(buffered_socket_domain,
+ "Input buffer is full");
+ OnSocketError(std::move(error));
+ return false;
+ }
+
+ ScheduleRead();
+ return true;
+
+ case InputResult::PAUSE:
+ CancelRead();
+ return true;
+
+ case InputResult::AGAIN:
+ continue;
+
+ case InputResult::CLOSED:
+ return false;
+ }
+ }
+}
+
+void
+BufferedSocket::ConsumeInput(size_t nbytes)
+{
+ assert(IsDefined());
+
+ fifo_buffer_consume(input, nbytes);
+}
+
+bool
+BufferedSocket::OnSocketReady(unsigned flags)
+{
+ assert(IsDefined());
+
+ if (gcc_unlikely(flags & (ERROR|HANGUP))) {
+ OnSocketClosed();
+ return false;
+ }
+
+ if (flags & READ) {
+ assert(input == nullptr || !fifo_buffer_is_full(input));
+
+ if (!ReadToBuffer() || !ResumeInput())
+ return false;
+
+ if (input == nullptr || !fifo_buffer_is_full(input))
+ ScheduleRead();
+ }
+
+ return true;
+}
diff --git a/src/event/BufferedSocket.hxx b/src/event/BufferedSocket.hxx
new file mode 100644
index 000000000..cc763c164
--- /dev/null
+++ b/src/event/BufferedSocket.hxx
@@ -0,0 +1,105 @@
+/*
+ * 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_BUFFERED_SOCKET_HXX
+#define MPD_BUFFERED_SOCKET_HXX
+
+#include "check.h"
+#include "SocketMonitor.hxx"
+#include "gcc.h"
+
+struct fifo_buffer;
+class Error;
+
+/**
+ * A #SocketMonitor specialization that adds an input buffer.
+ */
+class BufferedSocket : protected SocketMonitor {
+ fifo_buffer *input;
+
+public:
+ BufferedSocket(int _fd, EventLoop &_loop)
+ :SocketMonitor(_fd, _loop), input(nullptr) {
+ ScheduleRead();
+ }
+
+ ~BufferedSocket();
+
+ using SocketMonitor::IsDefined;
+ using SocketMonitor::Close;
+ using SocketMonitor::Write;
+
+private:
+ ssize_t DirectRead(void *data, size_t length);
+
+ /**
+ * Receive data from the socket to the input buffer.
+ *
+ * @return false if the socket has been closed
+ */
+ bool ReadToBuffer();
+
+protected:
+ /**
+ * @return false if the socket has been closed
+ */
+ bool ResumeInput();
+
+ /**
+ * Mark a portion of the input buffer "consumed". Only
+ * allowed to be called from OnSocketInput(). This method
+ * does not invalidate the pointer passed to OnSocketInput()
+ * yet.
+ */
+ void ConsumeInput(size_t nbytes);
+
+ enum class InputResult {
+ /**
+ * The method was successful, and it is ready to
+ * read more data.
+ */
+ MORE,
+
+ /**
+ * The method does not want to get more data for now.
+ * It will call ResumeInput() when it's ready for
+ * more.
+ */
+ PAUSE,
+
+ /**
+ * The method wants to be called again immediately, if
+ * there's more data in the buffer.
+ */
+ AGAIN,
+
+ /**
+ * The method has closed the socket.
+ */
+ CLOSED,
+ };
+
+ virtual InputResult OnSocketInput(const void *data, size_t length) = 0;
+ virtual void OnSocketError(Error &&error) = 0;
+ virtual void OnSocketClosed() = 0;
+
+ virtual bool OnSocketReady(unsigned flags) override;
+};
+
+#endif
diff --git a/src/event/Call.cxx b/src/event/Call.cxx
new file mode 100644
index 000000000..e7d963ac3
--- /dev/null
+++ b/src/event/Call.cxx
@@ -0,0 +1,102 @@
+/*
+ * 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"
+#include "Call.hxx"
+#include "Loop.hxx"
+#include "DeferredMonitor.hxx"
+#include "thread/Mutex.hxx"
+#include "thread/Cond.hxx"
+#include "gcc.h"
+
+#include <assert.h>
+
+class BlockingCallMonitor final
+#ifndef USE_EPOLL
+ : DeferredMonitor
+#endif
+{
+ const std::function<void()> f;
+
+ Mutex mutex;
+ Cond cond;
+
+ 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)
+ cond.wait(mutex);
+ mutex.unlock();
+ }
+
+#ifndef USE_EPOLL
+private:
+ virtual void RunDeferred() override {
+ DoRun();
+ }
+
+#else
+public:
+#endif
+ void DoRun() {
+ assert(!done);
+
+ f();
+
+ mutex.lock();
+ done = true;
+ cond.signal();
+ mutex.unlock();
+ }
+};
+
+void
+BlockingCall(EventLoop &loop, std::function<void()> &&f)
+{
+ if (loop.IsInside()) {
+ /* we're already inside the loop - we can simply call
+ the function */
+ f();
+ } else {
+ /* outside the EventLoop's thread - defer execution to
+ the EventLoop, wait for completion */
+ BlockingCallMonitor m(loop, std::move(f));
+ m.Run();
+ }
+}
diff --git a/src/event/Call.hxx b/src/event/Call.hxx
new file mode 100644
index 000000000..34d886ca5
--- /dev/null
+++ b/src/event/Call.hxx
@@ -0,0 +1,36 @@
+/*
+ * 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_CALL_HXX
+#define MPD_EVENT_CALL_HXX
+
+#include "check.h"
+
+#include <functional>
+
+class EventLoop;
+
+/**
+ * Call the given function in the context of the #EventLoop, and wait
+ * for it to finish.
+ */
+void
+BlockingCall(EventLoop &loop, std::function<void()> &&f);
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/DeferredMonitor.cxx b/src/event/DeferredMonitor.cxx
new file mode 100644
index 000000000..4ffffaa89
--- /dev/null
+++ b/src/event/DeferredMonitor.cxx
@@ -0,0 +1,81 @@
+/*
+ * 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"
+#include "DeferredMonitor.hxx"
+#include "Loop.hxx"
+
+void
+DeferredMonitor::Cancel()
+{
+#ifdef USE_EPOLL
+ pending = false;
+#else
+ const auto id = source_id.exchange(0);
+ if (id != 0)
+ g_source_remove(id);
+#endif
+}
+
+void
+DeferredMonitor::Schedule()
+{
+#ifdef USE_EPOLL
+ if (!pending.exchange(true))
+ fd.Write();
+#else
+ const unsigned id = loop.AddIdle(Callback, this);
+ const auto old_id = source_id.exchange(id);
+ if (old_id != 0)
+ g_source_remove(old_id);
+#endif
+}
+
+#ifdef USE_EPOLL
+
+bool
+DeferredMonitor::OnSocketReady(unsigned)
+{
+ fd.Read();
+
+ if (pending.exchange(false))
+ RunDeferred();
+
+ return true;
+}
+
+#else
+
+void
+DeferredMonitor::Run()
+{
+ const auto id = source_id.exchange(0);
+ if (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
new file mode 100644
index 000000000..988dce2d8
--- /dev/null
+++ b/src/event/DeferredMonitor.hxx
@@ -0,0 +1,98 @@
+/*
+ * 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_SOCKET_DEFERRED_MONITOR_HXX
+#define MPD_SOCKET_DEFERRED_MONITOR_HXX
+
+#include "check.h"
+#include "gcc.h"
+
+#ifdef USE_EPOLL
+#include "SocketMonitor.hxx"
+#include "WakeFD.hxx"
+#else
+#include <glib.h>
+#endif
+
+#include <atomic>
+
+class EventLoop;
+
+/**
+ * Defer execution of an event into an #EventLoop.
+ */
+class DeferredMonitor
+#ifdef USE_EPOLL
+ : private SocketMonitor
+#endif
+{
+#ifdef USE_EPOLL
+ std::atomic_bool pending;
+ WakeFD fd;
+#else
+ EventLoop &loop;
+
+ std::atomic<guint> source_id;
+#endif
+
+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
+
+ ~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();
+ void Cancel();
+
+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
new file mode 100644
index 000000000..3ffd9f416
--- /dev/null
+++ b/src/event/FullyBufferedSocket.cxx
@@ -0,0 +1,136 @@
+/*
+ * 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"
+#include "FullyBufferedSocket.hxx"
+#include "system/SocketError.hxx"
+#include "util/fifo_buffer.h"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+
+#ifndef WIN32
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+FullyBufferedSocket::ssize_t
+FullyBufferedSocket::DirectWrite(const void *data, size_t length)
+{
+ const auto nbytes = SocketMonitor::Write((const char *)data, length);
+ if (gcc_unlikely(nbytes < 0)) {
+ const auto code = GetSocketError();
+ if (IsSocketErrorAgain(code))
+ return 0;
+
+ Cancel();
+
+ if (IsSocketErrorClosed(code))
+ OnSocketClosed();
+ else
+ OnSocketError(NewSocketError(code));
+ }
+
+ return nbytes;
+}
+
+bool
+FullyBufferedSocket::WriteFromBuffer()
+{
+ assert(IsDefined());
+
+ size_t length;
+ const void *data = output.Read(&length);
+ if (data == nullptr) {
+ CancelWrite();
+ return true;
+ }
+
+ auto nbytes = DirectWrite(data, length);
+ if (gcc_unlikely(nbytes <= 0))
+ return nbytes == 0;
+
+ output.Consume(nbytes);
+
+ if (output.IsEmpty())
+ CancelWrite();
+
+ return true;
+}
+
+bool
+FullyBufferedSocket::Write(const void *data, size_t length)
+{
+ assert(IsDefined());
+
+#if 0
+ /* TODO: disabled because this would add overhead on some callers (the ones that often), but it may be useful */
+
+ if (output.IsEmpty()) {
+ /* try to write it directly first */
+ const auto nbytes = DirectWrite(data, length);
+ if (gcc_likely(nbytes > 0)) {
+ data = (const uint8_t *)data + nbytes;
+ length -= nbytes;
+ if (length == 0)
+ return true;
+ } else if (nbytes < 0)
+ return false;
+ }
+#endif
+
+ if (!output.Append(data, length)) {
+ // TODO
+ static constexpr Domain buffered_socket_domain("buffered_socket");
+ Error error;
+ error.Set(buffered_socket_domain, "Output buffer is full");
+ OnSocketError(std::move(error));
+ return false;
+ }
+
+ ScheduleWrite();
+ return true;
+}
+
+bool
+FullyBufferedSocket::OnSocketReady(unsigned flags)
+{
+ const bool was_empty = output.IsEmpty();
+ if (!BufferedSocket::OnSocketReady(flags))
+ return false;
+
+ if (was_empty && !output.IsEmpty())
+ /* just in case the OnSocketInput() method has added
+ data to the output buffer: try to send it now
+ instead of waiting for the next event loop
+ iteration */
+ flags |= WRITE;
+
+ if (flags & WRITE) {
+ assert(!output.IsEmpty());
+
+ if (!WriteFromBuffer())
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/event/FullyBufferedSocket.hxx b/src/event/FullyBufferedSocket.hxx
new file mode 100644
index 000000000..c67c2c78d
--- /dev/null
+++ b/src/event/FullyBufferedSocket.hxx
@@ -0,0 +1,63 @@
+/*
+ * 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_FULLY_BUFFERED_SOCKET_HXX
+#define MPD_FULLY_BUFFERED_SOCKET_HXX
+
+#include "check.h"
+#include "BufferedSocket.hxx"
+#include "util/PeakBuffer.hxx"
+#include "gcc.h"
+
+/**
+ * A #BufferedSocket specialization that adds an output buffer.
+ */
+class FullyBufferedSocket : protected BufferedSocket {
+ PeakBuffer output;
+
+public:
+ FullyBufferedSocket(int _fd, EventLoop &_loop,
+ size_t normal_size, size_t peak_size=0)
+ :BufferedSocket(_fd, _loop),
+ output(normal_size, peak_size) {
+ }
+
+ using BufferedSocket::IsDefined;
+ using BufferedSocket::Close;
+
+private:
+ ssize_t DirectWrite(const void *data, size_t length);
+
+ /**
+ * Send data from the output buffer to the socket.
+ *
+ * @return false if the socket has been closed
+ */
+ bool WriteFromBuffer();
+
+protected:
+ /**
+ * @return false if the socket has been closed
+ */
+ bool Write(const void *data, size_t length);
+
+ virtual bool OnSocketReady(unsigned flags) override;
+};
+
+#endif
diff --git a/src/event/IdleMonitor.cxx b/src/event/IdleMonitor.cxx
new file mode 100644
index 000000000..c99c66b26
--- /dev/null
+++ b/src/event/IdleMonitor.cxx
@@ -0,0 +1,84 @@
+/*
+ * 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"
+#include "IdleMonitor.hxx"
+#include "Loop.hxx"
+
+void
+IdleMonitor::Cancel()
+{
+ assert(loop.IsInside());
+
+ 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());
+
+ if (IsActive())
+ /* already scheduled */
+ return;
+
+#ifdef USE_EPOLL
+ active = true;
+ loop.AddIdle(*this);
+#else
+ source_id = loop.AddIdle(Callback, this);
+#endif
+}
+
+void
+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
new file mode 100644
index 000000000..c8e79eb1d
--- /dev/null
+++ b/src/event/IdleMonitor.hxx
@@ -0,0 +1,87 @@
+/*
+ * 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_SOCKET_IDLE_MONITOR_HXX
+#define MPD_SOCKET_IDLE_MONITOR_HXX
+
+#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.
+ */
+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();
+ }
+
+ EventLoop &GetEventLoop() const {
+ return loop;
+ }
+
+ bool IsActive() const {
+#ifdef USE_EPOLL
+ return active;
+#else
+ return source_id != 0;
+#endif
+ }
+
+ void Schedule();
+ void Cancel();
+
+protected:
+ virtual void OnIdle() = 0;
+
+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
new file mode 100644
index 000000000..faf967f21
--- /dev/null
+++ b/src/event/Loop.cxx
@@ -0,0 +1,259 @@
+/*
+ * 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"
+#include "Loop.hxx"
+#include "system/clock.h"
+
+#ifdef USE_EPOLL
+
+#include "TimeoutMonitor.hxx"
+#include "SocketMonitor.hxx"
+#include "IdleMonitor.hxx"
+
+#include <algorithm>
+
+EventLoop::EventLoop(Default)
+ :SocketMonitor(*this),
+ now_ms(::monotonic_clock_ms()),
+ quit(false),
+ n_events(0),
+ thread(ThreadId::Null())
+{
+ SocketMonitor::Open(wake_fd.Get());
+ SocketMonitor::Schedule(SocketMonitor::READ);
+}
+
+EventLoop::~EventLoop()
+{
+ assert(idle.empty());
+ assert(timers.empty());
+
+ /* avoid closing the WakeFD twice */
+ SocketMonitor::Steal();
+}
+
+void
+EventLoop::Break()
+{
+ if (IsInside())
+ quit = true;
+ else
+ AddCall([this]() { Break(); });
+}
+
+bool
+EventLoop::RemoveFD(int _fd, SocketMonitor &m)
+{
+ for (unsigned i = 0, n = n_events; i < n; ++i)
+ if (events[i].data.ptr == &m)
+ events[i].events = 0;
+
+ return epoll.Remove(_fd);
+}
+
+void
+EventLoop::AddIdle(IdleMonitor &i)
+{
+ assert(std::find(idle.begin(), idle.end(), &i) == idle.end());
+
+ idle.push_back(&i);
+}
+
+void
+EventLoop::RemoveIdle(IdleMonitor &i)
+{
+ auto it = std::find(idle.begin(), idle.end(), &i);
+ assert(it != idle.end());
+
+ idle.erase(it);
+}
+
+void
+EventLoop::AddTimer(TimeoutMonitor &t, unsigned ms)
+{
+ timers.insert(TimerRecord(t, now_ms + ms));
+}
+
+void
+EventLoop::CancelTimer(TimeoutMonitor &t)
+{
+ for (auto i = timers.begin(), end = timers.end(); i != end; ++i) {
+ if (&i->timer == &t) {
+ timers.erase(i);
+ return;
+ }
+ }
+}
+
+#endif
+
+void
+EventLoop::Run()
+{
+ assert(thread.IsNull());
+ thread = ThreadId::GetCurrent();
+
+#ifdef USE_EPOLL
+ assert(!quit);
+
+ do {
+ now_ms = ::monotonic_clock_ms();
+
+ /* invoke timers */
+
+ int timeout_ms;
+ while (true) {
+ auto i = timers.begin();
+ if (i == timers.end()) {
+ timeout_ms = -1;
+ break;
+ }
+
+ timeout_ms = i->due_ms - now_ms;
+ if (timeout_ms > 0)
+ break;
+
+ TimeoutMonitor &m = i->timer;
+ timers.erase(i);
+
+ m.Run();
+
+ if (quit)
+ return;
+ }
+
+ /* invoke idle */
+
+ const bool idle_empty = idle.empty();
+ while (!idle.empty()) {
+ IdleMonitor &m = *idle.front();
+ idle.pop_front();
+ m.Run();
+
+ if (quit)
+ return;
+ }
+
+ if (!idle_empty)
+ /* re-evaluate timers because one of the
+ IdleMonitors may have added a new
+ timeout */
+ continue;
+
+ /* wait for new event */
+
+ const int n = epoll.Wait(events, MAX_EVENTS, timeout_ms);
+ n_events = std::max(n, 0);
+
+ now_ms = ::monotonic_clock_ms();
+
+ 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);
+
+ if (quit)
+ break;
+ }
+ }
+
+ n_events = 0;
+ } while (!quit);
+#else
+ g_main_loop_run(loop);
+#endif
+
+ assert(thread.IsInside());
+}
+
+#ifdef USE_EPOLL
+
+void
+EventLoop::AddCall(std::function<void()> &&f)
+{
+ mutex.lock();
+ calls.push_back(f);
+ mutex.unlock();
+
+ wake_fd.Write();
+}
+
+bool
+EventLoop::OnSocketReady(gcc_unused unsigned flags)
+{
+ assert(!quit);
+
+ wake_fd.Read();
+
+ mutex.lock();
+
+ while (!calls.empty() && !quit) {
+ auto f = std::move(calls.front());
+ calls.pop_front();
+
+ mutex.unlock();
+ f();
+ mutex.lock();
+ }
+
+ mutex.unlock();
+
+ return true;
+}
+
+#else
+
+guint
+EventLoop::AddIdle(GSourceFunc function, gpointer data)
+{
+ GSource *source = g_idle_source_new();
+ g_source_set_callback(source, function, data, NULL);
+ guint id = g_source_attach(source, GetContext());
+ g_source_unref(source);
+ return id;
+}
+
+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;
+}
+
+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;
+}
+
+#endif
diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx
new file mode 100644
index 000000000..ec90cdacf
--- /dev/null
+++ b/src/event/Loop.hxx
@@ -0,0 +1,191 @@
+/*
+ * 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_LOOP_HXX
+#define MPD_EVENT_LOOP_HXX
+
+#include "check.h"
+#include "thread/Id.hxx"
+#include "gcc.h"
+
+#ifdef USE_EPOLL
+#include "system/EPollFD.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 SocketMonitor;
+#endif
+
+#include <assert.h>
+
+class EventLoop final
+#ifdef USE_EPOLL
+ : private SocketMonitor
+#endif
+{
+#ifdef USE_EPOLL
+ struct TimerRecord {
+ /**
+ * Projected monotonic_clock_ms() value when this
+ * timer is due.
+ */
+ const unsigned due_ms;
+
+ TimeoutMonitor &timer;
+
+ constexpr TimerRecord(TimeoutMonitor &_timer,
+ unsigned _due_ms)
+ :due_ms(_due_ms), timer(_timer) {}
+
+ bool operator<(const TimerRecord &other) const {
+ return due_ms < other.due_ms;
+ }
+
+ bool IsDue(unsigned _now_ms) const {
+ return _now_ms >= due_ms;
+ }
+ };
+
+ EPollFD epoll;
+
+ WakeFD wake_fd;
+
+ std::multiset<TimerRecord> timers;
+ std::list<IdleMonitor *> idle;
+
+ Mutex mutex;
+ std::list<std::function<void()>> calls;
+
+ unsigned now_ms;
+
+ bool quit;
+
+ static constexpr unsigned MAX_EVENTS = 16;
+ unsigned n_events;
+ epoll_event events[MAX_EVENTS];
+#else
+ GMainContext *context;
+ GMainLoop *loop;
+#endif
+
+ /**
+ * A reference to the thread that is currently inside Run().
+ */
+ ThreadId thread;
+
+public:
+#ifdef USE_EPOLL
+ struct Default {};
+
+ EventLoop(Default dummy=Default());
+ ~EventLoop();
+
+ unsigned GetTimeMS() const {
+ return now_ms;
+ }
+
+ void Break();
+
+ bool AddFD(int _fd, unsigned flags, SocketMonitor &m) {
+ return epoll.Add(_fd, flags, &m);
+ }
+
+ bool ModifyFD(int _fd, unsigned flags, SocketMonitor &m) {
+ return epoll.Modify(_fd, flags, &m);
+ }
+
+ bool RemoveFD(int fd, SocketMonitor &m);
+
+ void AddIdle(IdleMonitor &i);
+ void RemoveIdle(IdleMonitor &i);
+
+ void AddTimer(TimeoutMonitor &t, unsigned ms);
+ void CancelTimer(TimeoutMonitor &t);
+
+ void AddCall(std::function<void()> &&f);
+
+ void Run();
+
+private:
+ 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?
+ */
+ gcc_pure
+ bool IsInside() const {
+ assert(!thread.IsNull());
+
+ return thread.IsInside();
+ }
+};
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/MultiSocketMonitor.cxx b/src/event/MultiSocketMonitor.cxx
new file mode 100644
index 000000000..2ebad02e5
--- /dev/null
+++ b/src/event/MultiSocketMonitor.cxx
@@ -0,0 +1,163 @@
+/*
+ * 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"
+#include "MultiSocketMonitor.hxx"
+#include "Loop.hxx"
+#include "system/fd_util.h"
+#include "gcc.h"
+
+#include <assert.h>
+
+#ifdef USE_EPOLL
+
+MultiSocketMonitor::MultiSocketMonitor(EventLoop &_loop)
+ :IdleMonitor(_loop), TimeoutMonitor(_loop), ready(false) {
+}
+
+MultiSocketMonitor::~MultiSocketMonitor()
+{
+ // TODO
+}
+
+void
+MultiSocketMonitor::Prepare()
+{
+ int timeout_ms = PrepareSockets();
+ if (timeout_ms >= 0)
+ TimeoutMonitor::Schedule(timeout_ms);
+ else
+ TimeoutMonitor::Cancel();
+
+}
+
+void
+MultiSocketMonitor::OnIdle()
+{
+ if (ready) {
+ ready = false;
+ DispatchSockets();
+
+ /* TODO: don't refresh always; require users to call
+ InvalidateSockets() */
+ refresh = true;
+ }
+
+ if (refresh) {
+ refresh = false;
+ 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
new file mode 100644
index 000000000..fe74206a3
--- /dev/null
+++ b/src/event/MultiSocketMonitor.hxx
@@ -0,0 +1,254 @@
+/*
+ * 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_MULTI_SOCKET_MONITOR_HXX
+#define MPD_MULTI_SOCKET_MONITOR_HXX
+
+#include "check.h"
+#include "gcc.h"
+
+#ifdef USE_EPOLL
+#include "IdleMonitor.hxx"
+#include "TimeoutMonitor.hxx"
+#include "SocketMonitor.hxx"
+#else
+#include "glib_compat.h"
+#include <glib.h>
+
+#endif
+
+#include <forward_list>
+
+#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 */
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+class EventLoop;
+
+/**
+ * Monitor multiple sockets.
+ */
+class MultiSocketMonitor
+#ifdef USE_EPOLL
+ : private IdleMonitor, private TimeoutMonitor
+#endif
+{
+#ifdef USE_EPOLL
+ class SingleFD final : public SocketMonitor {
+ MultiSocketMonitor &multi;
+
+ unsigned revents;
+
+ public:
+ SingleFD(MultiSocketMonitor &_multi, int _fd, unsigned events)
+ :SocketMonitor(_fd, _multi.GetEventLoop()),
+ multi(_multi), revents(0) {
+ Schedule(events);
+ }
+
+ int GetFD() const {
+ return SocketMonitor::Get();
+ }
+
+ unsigned GetEvents() const {
+ return SocketMonitor::GetScheduledFlags();
+ }
+
+ void SetEvents(unsigned _events) {
+ revents &= _events;
+ SocketMonitor::Schedule(_events);
+ }
+
+ unsigned GetReturnedEvents() const {
+ return revents;
+ }
+
+ void ClearReturnedEvents() {
+ revents = 0;
+ }
+
+ protected:
+ virtual bool OnSocketReady(unsigned flags) override {
+ revents = flags;
+ multi.SetReady();
+ return true;
+ }
+ };
+
+ 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
+
+ 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
+ }
+
+ template<typename E>
+ void UpdateSocketList(E &&e) {
+ for (auto prev = fds.before_begin(), end = fds.end(),
+ i = std::next(prev);
+ i != end; i = std::next(prev)) {
+ assert(i->GetEvents() != 0);
+
+ unsigned events = e(i->GetFD());
+ if (events != 0) {
+ 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);
+ }
+ }
+ }
+
+protected:
+ /**
+ * @return timeout [ms] or -1 for no timeout
+ */
+ virtual int PrepareSockets() = 0;
+ virtual void DispatchSockets() = 0;
+
+#ifdef USE_EPOLL
+private:
+ void SetReady() {
+ ready = true;
+ IdleMonitor::Schedule();
+ }
+
+ void Prepare();
+
+ virtual void OnTimeout() final {
+ SetReady();
+ IdleMonitor::Schedule();
+ }
+
+ 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/ServerSocket.cxx b/src/event/ServerSocket.cxx
new file mode 100644
index 000000000..342acc04e
--- /dev/null
+++ b/src/event/ServerSocket.cxx
@@ -0,0 +1,434 @@
+/*
+ * 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 HAVE_STRUCT_UCRED
+#define _GNU_SOURCE 1
+#endif
+
+#include "ServerSocket.hxx"
+#include "system/SocketUtil.hxx"
+#include "system/SocketError.hxx"
+#include "event/SocketMonitor.hxx"
+#include "system/Resolver.hxx"
+#include "system/fd_util.h"
+#include "util/Error.hxx"
+#include "util/Domain.hxx"
+
+#include <glib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#ifdef WIN32
+#include <ws2tcpip.h>
+#include <winsock.h>
+#else
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netdb.h>
+#endif
+
+#undef G_LOG_DOMAIN
+#define G_LOG_DOMAIN "listen"
+
+#define DEFAULT_PORT 6600
+
+class OneServerSocket final : private SocketMonitor {
+ ServerSocket &parent;
+
+ const unsigned serial;
+
+ char *path;
+
+ size_t address_length;
+ struct sockaddr *address;
+
+public:
+ OneServerSocket(EventLoop &_loop, ServerSocket &_parent,
+ unsigned _serial,
+ const struct sockaddr *_address,
+ size_t _address_length)
+ :SocketMonitor(_loop),
+ parent(_parent), serial(_serial),
+ path(nullptr),
+ address_length(_address_length),
+ address((sockaddr *)g_memdup(_address, _address_length))
+ {
+ assert(_address != nullptr);
+ assert(_address_length > 0);
+ }
+
+ OneServerSocket(const OneServerSocket &other) = delete;
+ OneServerSocket &operator=(const OneServerSocket &other) = delete;
+
+ ~OneServerSocket() {
+ g_free(path);
+ g_free(address);
+ }
+
+ unsigned GetSerial() const {
+ return serial;
+ }
+
+ void SetPath(const char *_path) {
+ assert(path == nullptr);
+
+ path = g_strdup(_path);
+ }
+
+ bool Open(Error &error);
+
+ using SocketMonitor::IsDefined;
+ using SocketMonitor::Close;
+
+ char *ToString() const;
+
+ void SetFD(int _fd) {
+ SocketMonitor::Open(_fd);
+ SocketMonitor::ScheduleRead();
+ }
+
+ void Accept();
+
+private:
+ virtual bool OnSocketReady(unsigned flags) override;
+};
+
+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)
+{
+#ifdef HAVE_STRUCT_UCRED
+ struct ucred cred;
+ socklen_t len = sizeof (cred);
+
+ if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) < 0)
+ return 0;
+
+ return cred.uid;
+#else
+#ifdef HAVE_GETPEEREID
+ uid_t euid;
+ gid_t egid;
+
+ if (getpeereid(fd, &euid, &egid) == 0)
+ return euid;
+#else
+ (void)fd;
+#endif
+ return -1;
+#endif
+}
+
+inline void
+OneServerSocket::Accept()
+{
+ struct sockaddr_storage peer_address;
+ size_t peer_address_length = sizeof(peer_address);
+ int peer_fd =
+ accept_cloexec_nonblock(Get(), (struct sockaddr*)&peer_address,
+ &peer_address_length);
+ if (peer_fd < 0) {
+ const SocketErrorMessage msg;
+ g_warning("accept() failed: %s", (const char *)msg);
+ return;
+ }
+
+ if (socket_keepalive(peer_fd)) {
+ const SocketErrorMessage msg;
+ g_warning("Could not set TCP keepalive option: %s",
+ (const char *)msg);
+ }
+
+ parent.OnAccept(peer_fd,
+ (const sockaddr &)peer_address,
+ peer_address_length, get_remote_uid(peer_fd));
+}
+
+bool
+OneServerSocket::OnSocketReady(gcc_unused unsigned flags)
+{
+ Accept();
+ return true;
+}
+
+inline bool
+OneServerSocket::Open(Error &error)
+{
+ assert(!IsDefined());
+
+ int _fd = socket_bind_listen(address->sa_family,
+ SOCK_STREAM, 0,
+ address, address_length, 5,
+ error);
+ if (_fd < 0)
+ return false;
+
+ /* allow everybody to connect */
+
+ if (path != nullptr)
+ chmod(path, 0666);
+
+ /* register in the GLib main loop */
+
+ SetFD(_fd);
+
+ return true;
+}
+
+ServerSocket::ServerSocket(EventLoop &_loop)
+ :loop(_loop), next_serial(1) {}
+
+/* this is just here to allow the OneServerSocket forward
+ declaration */
+ServerSocket::~ServerSocket() {}
+
+bool
+ServerSocket::Open(Error &error)
+{
+ OneServerSocket *good = nullptr, *bad = nullptr;
+ Error last_error;
+
+ for (auto &i : sockets) {
+ assert(i.GetSerial() > 0);
+ assert(good == nullptr || i.GetSerial() <= good->GetSerial());
+
+ if (bad != nullptr && i.GetSerial() != bad->GetSerial()) {
+ Close();
+ error = std::move(last_error);
+ return false;
+ }
+
+ Error error2;
+ if (!i.Open(error2)) {
+ if (good != nullptr && good->GetSerial() == i.GetSerial()) {
+ char *address_string = i.ToString();
+ char *good_string = good->ToString();
+ g_warning("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);
+ } else if (bad == nullptr) {
+ bad = &i;
+
+ char *address_string = i.ToString();
+ error2.FormatPrefix("Failed to bind to '%s': ",
+ address_string);
+ g_free(address_string);
+
+ last_error = std::move(error2);
+ }
+
+ continue;
+ }
+
+ /* mark this socket as "good", and clear previous
+ errors */
+
+ good = &i;
+
+ if (bad != nullptr) {
+ bad = nullptr;
+ last_error.Clear();
+ }
+ }
+
+ if (bad != nullptr) {
+ Close();
+ error = std::move(last_error);
+ return false;
+ }
+
+ return true;
+}
+
+void
+ServerSocket::Close()
+{
+ for (auto &i : sockets)
+ if (i.IsDefined())
+ i.Close();
+}
+
+OneServerSocket &
+ServerSocket::AddAddress(const sockaddr &address, size_t address_length)
+{
+ sockets.emplace_front(loop, *this, next_serial,
+ &address, address_length);
+
+ return sockets.front();
+}
+
+bool
+ServerSocket::AddFD(int fd, Error &error)
+{
+ assert(fd >= 0);
+
+ struct sockaddr_storage address;
+ socklen_t address_length = sizeof(address);
+ if (getsockname(fd, (struct sockaddr *)&address,
+ &address_length) < 0) {
+ SetSocketError(error);
+ error.AddPrefix("Failed to get socket address: ");
+ return false;
+ }
+
+ OneServerSocket &s = AddAddress((const sockaddr &)address,
+ address_length);
+ s.SetFD(fd);
+
+ return true;
+}
+
+#ifdef HAVE_TCP
+
+inline void
+ServerSocket::AddPortIPv4(unsigned port)
+{
+ struct sockaddr_in sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_port = htons(port);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ AddAddress((const sockaddr &)sin, sizeof(sin));
+}
+
+#ifdef HAVE_IPV6
+inline void
+ServerSocket::AddPortIPv6(unsigned port)
+{
+ struct sockaddr_in6 sin;
+ memset(&sin, 0, sizeof(sin));
+ sin.sin6_port = htons(port);
+ sin.sin6_family = AF_INET6;
+
+ AddAddress((const sockaddr &)sin, sizeof(sin));
+}
+#endif /* HAVE_IPV6 */
+
+#endif /* HAVE_TCP */
+
+bool
+ServerSocket::AddPort(unsigned port, Error &error)
+{
+#ifdef HAVE_TCP
+ if (port == 0 || port > 0xffff) {
+ error.Set(server_socket_domain, "Invalid TCP port");
+ return false;
+ }
+
+#ifdef HAVE_IPV6
+ AddPortIPv6(port);
+#endif
+ AddPortIPv4(port);
+
+ ++next_serial;
+
+ return true;
+#else /* HAVE_TCP */
+ (void)port;
+
+ error.Set(server_socket_domain, "TCP support is disabled");
+ return false;
+#endif /* HAVE_TCP */
+}
+
+bool
+ServerSocket::AddHost(const char *hostname, unsigned port, Error &error)
+{
+#ifdef HAVE_TCP
+ struct addrinfo *ai = resolve_host_port(hostname, port,
+ AI_PASSIVE, SOCK_STREAM,
+ error);
+ if (ai == nullptr)
+ return false;
+
+ for (const struct addrinfo *i = ai; i != nullptr; i = i->ai_next)
+ AddAddress(*i->ai_addr, i->ai_addrlen);
+
+ freeaddrinfo(ai);
+
+ ++next_serial;
+
+ return true;
+#else /* HAVE_TCP */
+ (void)hostname;
+ (void)port;
+
+ error.Set(server_socket_domain, "TCP support is disabled");
+ return false;
+#endif /* HAVE_TCP */
+}
+
+bool
+ServerSocket::AddPath(const char *path, Error &error)
+{
+#ifdef HAVE_UN
+ struct sockaddr_un s_un;
+
+ size_t path_length = strlen(path);
+ if (path_length >= sizeof(s_un.sun_path)) {
+ error.Set(server_socket_domain,
+ "UNIX socket path is too long");
+ return false;
+ }
+
+ unlink(path);
+
+ s_un.sun_family = AF_UNIX;
+ memcpy(s_un.sun_path, path, path_length + 1);
+
+ OneServerSocket &s = AddAddress((const sockaddr &)s_un, sizeof(s_un));
+ s.SetPath(path);
+
+ return true;
+#else /* !HAVE_UN */
+ (void)path;
+
+ error.Set(server_socket_domain,
+ "UNIX domain socket support is disabled");
+ return false;
+#endif /* !HAVE_UN */
+}
+
diff --git a/src/event/ServerSocket.hxx b/src/event/ServerSocket.hxx
new file mode 100644
index 000000000..9f0745708
--- /dev/null
+++ b/src/event/ServerSocket.hxx
@@ -0,0 +1,120 @@
+/*
+ * 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_SERVER_SOCKET_HXX
+#define MPD_SERVER_SOCKET_HXX
+
+#include <forward_list>
+
+#include <stddef.h>
+
+struct sockaddr;
+class EventLoop;
+class Error;
+
+typedef void (*server_socket_callback_t)(int fd,
+ const struct sockaddr *address,
+ size_t address_length, int uid,
+ void *ctx);
+
+class OneServerSocket;
+
+class ServerSocket {
+ friend class OneServerSocket;
+
+ EventLoop &loop;
+
+ std::forward_list<OneServerSocket> sockets;
+
+ unsigned next_serial;
+
+public:
+ ServerSocket(EventLoop &_loop);
+ ~ServerSocket();
+
+ EventLoop &GetEventLoop() {
+ return loop;
+ }
+
+private:
+ OneServerSocket &AddAddress(const sockaddr &address, size_t length);
+
+ /**
+ * Add a listener on a port on all IPv4 interfaces.
+ *
+ * @param port the TCP port
+ */
+ void AddPortIPv4(unsigned port);
+
+ /**
+ * Add a listener on a port on all IPv6 interfaces.
+ *
+ * @param port the TCP port
+ */
+ void AddPortIPv6(unsigned port);
+
+public:
+ /**
+ * Add a listener on a port on all interfaces.
+ *
+ * @param port the TCP port
+ * @param error_r location to store the error occurring, or NULL to
+ * ignore errors
+ * @return true on success
+ */
+ bool AddPort(unsigned port, Error &error);
+
+ /**
+ * Resolves a host name, and adds listeners on all addresses in the
+ * result set.
+ *
+ * @param hostname the host name to be resolved
+ * @param port the TCP port
+ * @param error_r location to store the error occurring, or NULL to
+ * ignore errors
+ * @return true on success
+ */
+ bool AddHost(const char *hostname, unsigned port, Error &error);
+
+ /**
+ * Add a listener on a Unix domain socket.
+ *
+ * @param path the absolute socket path
+ * @param error_r location to store the error occurring, or NULL to
+ * ignore errors
+ * @return true on success
+ */
+ bool AddPath(const char *path, Error &error);
+
+ /**
+ * Add a socket descriptor that is accepting connections. After this
+ * has been called, don't call server_socket_open(), because the
+ * socket is already open.
+ */
+ bool AddFD(int fd, Error &error);
+
+ bool Open(Error &error);
+ void Close();
+
+protected:
+ virtual void OnAccept(int fd, const sockaddr &address,
+ size_t address_length, int uid) = 0;
+};
+
+#endif
diff --git a/src/event/SignalMonitor.cxx b/src/event/SignalMonitor.cxx
new file mode 100644
index 000000000..ee72dea72
--- /dev/null
+++ b/src/event/SignalMonitor.cxx
@@ -0,0 +1,204 @@
+/*
+ * 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"
+#include "SignalMonitor.hxx"
+
+#ifndef WIN32
+
+#include "WakeFD.hxx"
+#include "SocketMonitor.hxx"
+#include "util/Manual.hxx"
+#include "system/FatalError.hxx"
+
+#ifdef USE_SIGNALFD
+#include "system/SignalFD.hxx"
+#else
+#include "WakeFD.hxx"
+#endif
+
+#ifndef USE_SIGNALFD
+#include <atomic>
+#endif
+
+#include <algorithm>
+
+class SignalMonitor final : private SocketMonitor {
+#ifdef USE_SIGNALFD
+ SignalFD fd;
+#else
+ WakeFD fd;
+#endif
+
+public:
+ SignalMonitor(EventLoop &_loop)
+ :SocketMonitor(_loop) {
+#ifndef USE_SIGNALFD
+ SocketMonitor::Open(fd.Get());
+ SocketMonitor::ScheduleRead();
+#endif
+ }
+
+ ~SignalMonitor() {
+ /* prevent the descriptor to be closed twice */
+#ifdef USE_SIGNALFD
+ if (SocketMonitor::IsDefined())
+#endif
+ SocketMonitor::Steal();
+ }
+
+#ifdef USE_SIGNALFD
+ void Update(sigset_t &mask) {
+ const bool was_open = SocketMonitor::IsDefined();
+
+ fd.Create(mask);
+
+ if (!was_open) {
+ SocketMonitor::Open(fd.Get());
+ SocketMonitor::ScheduleRead();
+ }
+ }
+#else
+ void WakeUp() {
+ fd.Write();
+ }
+#endif
+
+private:
+ virtual bool OnSocketReady(unsigned flags) override;
+};
+
+/* this should be enough - is it? */
+static constexpr unsigned MAX_SIGNAL = 64;
+
+static SignalHandler signal_handlers[MAX_SIGNAL];
+
+#ifdef USE_SIGNALFD
+static sigset_t signal_mask;
+#else
+static std::atomic_bool signal_pending[MAX_SIGNAL];
+#endif
+
+static Manual<SignalMonitor> monitor;
+
+#ifndef USE_SIGNALFD
+static void
+SignalCallback(int signo)
+{
+ assert(signal_handlers[signo] != nullptr);
+
+ if (!signal_pending[signo].exchange(true))
+ monitor->WakeUp();
+}
+#endif
+
+void
+SignalMonitorInit(EventLoop &loop)
+{
+#ifdef USE_SIGNALFD
+ sigemptyset(&signal_mask);
+#endif
+
+ monitor.Construct(loop);
+}
+
+#ifndef USE_SIGNALFD
+
+static void
+x_sigaction(int signum, const struct sigaction &act)
+{
+ if (sigaction(signum, &act, nullptr) < 0)
+ FatalSystemError("sigaction() failed");
+}
+
+#endif
+
+void
+SignalMonitorFinish()
+{
+#ifdef USE_SIGNALFD
+ std::fill_n(signal_handlers, MAX_SIGNAL, nullptr);
+#else
+ struct sigaction sa;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_DFL;
+
+ for (unsigned i = 0; i < MAX_SIGNAL; ++i) {
+ if (signal_handlers[i] != nullptr) {
+ x_sigaction(i, sa);
+ signal_handlers[i] = nullptr;
+ }
+ }
+
+ std::fill_n(signal_pending, MAX_SIGNAL, false);
+#endif
+
+ monitor.Destruct();
+}
+
+void
+SignalMonitorRegister(int signo, SignalHandler handler)
+{
+ assert(signal_handlers[signo] == nullptr);
+#ifndef USE_SIGNALFD
+ assert(!signal_pending[signo]);
+#endif
+
+ signal_handlers[signo] = handler;
+
+#ifdef USE_SIGNALFD
+ sigaddset(&signal_mask, signo);
+
+ if (sigprocmask(SIG_BLOCK, &signal_mask, nullptr) < 0)
+ FatalSystemError("sigprocmask() failed");
+
+ monitor->Update(signal_mask);
+#else
+ struct sigaction sa;
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SignalCallback;
+ x_sigaction(signo, sa);
+#endif
+}
+
+bool
+SignalMonitor::OnSocketReady(unsigned)
+{
+#ifdef USE_SIGNALFD
+ int signo;
+ while ((signo = fd.Read()) >= 0) {
+ assert(unsigned(signo) < MAX_SIGNAL);
+ assert(signal_handlers[signo] != nullptr);
+
+ signal_handlers[signo]();
+ }
+#else
+ fd.Read();
+
+ for (unsigned i = 0; i < MAX_SIGNAL; ++i)
+ if (signal_pending[i].exchange(false))
+ signal_handlers[i]();
+#endif
+
+ return true;
+}
+
+#endif
diff --git a/src/event/SignalMonitor.hxx b/src/event/SignalMonitor.hxx
new file mode 100644
index 000000000..94ab0aa30
--- /dev/null
+++ b/src/event/SignalMonitor.hxx
@@ -0,0 +1,64 @@
+/*
+ * 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_SOCKET_SIGNAL_MONITOR_HXX
+#define MPD_SOCKET_SIGNAL_MONITOR_HXX
+
+#include "check.h"
+
+class EventLoop;
+
+#ifndef WIN32
+
+typedef void (*SignalHandler)();
+
+/**
+ * Initialise the signal monitor subsystem.
+ */
+void
+SignalMonitorInit(EventLoop &loop);
+
+/**
+ * Deinitialise the signal monitor subsystem.
+ */
+void
+SignalMonitorFinish();
+
+/**
+ * Register a handler for the specified signal. The handler will be
+ * invoked in a safe context.
+ */
+void
+SignalMonitorRegister(int signo, SignalHandler handler);
+
+#else
+
+static inline void
+SignalMonitorInit(EventLoop &)
+{
+}
+
+static inline void
+SignalMonitorFinish()
+{
+}
+
+#endif
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/event/SocketMonitor.cxx b/src/event/SocketMonitor.cxx
new file mode 100644
index 000000000..76dab9346
--- /dev/null
+++ b/src/event/SocketMonitor.cxx
@@ -0,0 +1,212 @@
+/*
+ * 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"
+#include "SocketMonitor.hxx"
+#include "Loop.hxx"
+#include "system/fd_util.h"
+#include "gcc.h"
+
+#include <assert.h>
+
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/types.h>
+#include <sys/socket.h>
+#endif
+
+#ifdef USE_EPOLL
+
+void
+SocketMonitor::Dispatch(unsigned flags)
+{
+ flags &= GetScheduledFlags();
+
+ if (flags != 0 && !OnSocketReady(flags) && IsDefined())
+ 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();
+}
+
+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
+SocketMonitor::Steal()
+{
+ assert(IsDefined());
+
+ Cancel();
+
+ int result = fd;
+ fd = -1;
+
+#ifndef USE_EPOLL
+ g_source_destroy(&source->base);
+ g_source_unref(&source->base);
+ source = nullptr;
+#endif
+
+ return result;
+}
+
+void
+SocketMonitor::Close()
+{
+ close_socket(Steal());
+}
+
+void
+SocketMonitor::Schedule(unsigned flags)
+{
+ assert(IsDefined());
+
+ if (flags == GetScheduledFlags())
+ return;
+
+#ifdef USE_EPOLL
+ if (scheduled_flags == 0)
+ loop.AddFD(fd, flags, *this);
+ else if (flags == 0)
+ loop.RemoveFD(fd, *this);
+ else
+ loop.ModifyFD(fd, flags, *this);
+
+ scheduled_flags = flags;
+#else
+ poll.events = flags;
+ poll.revents &= flags;
+
+ loop.WakeUp();
+#endif
+}
+
+SocketMonitor::ssize_t
+SocketMonitor::Read(void *data, size_t length)
+{
+ assert(IsDefined());
+
+ int flags = 0;
+#ifdef MSG_DONTWAIT
+ flags |= MSG_DONTWAIT;
+#endif
+
+ return recv(Get(), (char *)data, length, flags);
+}
+
+SocketMonitor::ssize_t
+SocketMonitor::Write(const void *data, size_t length)
+{
+ assert(IsDefined());
+
+ int flags = 0;
+#ifdef MSG_NOSIGNAL
+ flags |= MSG_NOSIGNAL;
+#endif
+#ifdef MSG_DONTWAIT
+ flags |= MSG_DONTWAIT;
+#endif
+
+ return send(Get(), (const char *)data, length, flags);
+}
diff --git a/src/event/SocketMonitor.hxx b/src/event/SocketMonitor.hxx
new file mode 100644
index 000000000..d6efeac17
--- /dev/null
+++ b/src/event/SocketMonitor.hxx
@@ -0,0 +1,189 @@
+/*
+ * 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_SOCKET_MONITOR_HXX
+#define MPD_SOCKET_MONITOR_HXX
+
+#include "check.h"
+
+#ifdef USE_EPOLL
+#include <sys/epoll.h>
+#else
+#include <glib.h>
+#endif
+
+#include <type_traits>
+
+#include <assert.h>
+#include <stddef.h>
+
+#ifdef WIN32
+/* ERRORis a WIN32 macro that poisons our namespace; this is a
+ kludge to allow us to use it anyway */
+#ifdef ERROR
+#undef ERROR
+#endif
+#endif
+
+class EventLoop;
+
+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
+
+ 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();
+
+ EventLoop &GetEventLoop() {
+ return loop;
+ }
+
+ bool IsDefined() const {
+ return fd >= 0;
+ }
+
+ int Get() const {
+ assert(IsDefined());
+
+ return fd;
+ }
+
+ void Open(int _fd);
+
+ /**
+ * "Steal" the socket descriptor. This abandons the socket
+ * and puts the responsibility for closing it to the caller.
+ */
+ int Steal();
+
+ void Close();
+
+ unsigned GetScheduledFlags() const {
+ assert(IsDefined());
+
+#ifdef USE_EPOLL
+ return scheduled_flags;
+#else
+ return poll.events;
+#endif
+ }
+
+ void Schedule(unsigned flags);
+
+ void Cancel() {
+ Schedule(0);
+ }
+
+ void ScheduleRead() {
+ Schedule(GetScheduledFlags() | READ | HANGUP | ERROR);
+ }
+
+ void ScheduleWrite() {
+ Schedule(GetScheduledFlags() | WRITE);
+ }
+
+ void CancelRead() {
+ Schedule(GetScheduledFlags() & ~(READ|HANGUP|ERROR));
+ }
+
+ void CancelWrite() {
+ Schedule(GetScheduledFlags() & ~WRITE);
+ }
+
+ ssize_t Read(void *data, size_t length);
+ ssize_t Write(const void *data, size_t length);
+
+protected:
+ /**
+ * @return false if the socket has been closed
+ */
+ 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
new file mode 100644
index 000000000..cffad6b92
--- /dev/null
+++ b/src/event/TimeoutMonitor.cxx
@@ -0,0 +1,84 @@
+/*
+ * 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"
+#include "TimeoutMonitor.hxx"
+#include "Loop.hxx"
+
+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
+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
new file mode 100644
index 000000000..98e4e5564
--- /dev/null
+++ b/src/event/TimeoutMonitor.hxx
@@ -0,0 +1,85 @@
+/*
+ * 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_SOCKET_TIMEOUT_MONITOR_HXX
+#define MPD_SOCKET_TIMEOUT_MONITOR_HXX
+
+#include "check.h"
+
+#ifndef USE_EPOLL
+#include <glib.h>
+#endif
+
+class EventLoop;
+
+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();
+ }
+
+ EventLoop &GetEventLoop() {
+ return loop;
+ }
+
+ bool IsActive() const {
+#ifdef USE_EPOLL
+ return active;
+#else
+ return source != nullptr;
+#endif
+ }
+
+ void Schedule(unsigned ms);
+ void ScheduleSeconds(unsigned s);
+ void Cancel();
+
+protected:
+ virtual void OnTimeout() = 0;
+
+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
new file mode 100644
index 000000000..ed1baafd8
--- /dev/null
+++ b/src/event/WakeFD.hxx
@@ -0,0 +1,35 @@
+/*
+ * 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_WAKE_FD_HXX
+#define MPD_WAKE_FD_HXX
+
+#include "check.h"
+
+#include <assert.h>
+
+#ifdef USE_EVENTFD
+#include "system/EventFD.hxx"
+#define WakeFD EventFD
+#else
+#include "system/EventPipe.hxx"
+#define WakeFD EventPipe
+#endif
+
+#endif /* MAIN_NOTIFY_H */
diff --git a/src/event_pipe.c b/src/event_pipe.c
deleted file mode 100644
index d5c3b9564..000000000
--- a/src/event_pipe.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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"
-#include "event_pipe.h"
-#include "fd_util.h"
-#include "mpd_error.h"
-
-#include <stdbool.h>
-#include <assert.h>
-#include <glib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#ifdef WIN32
-/* for _O_BINARY */
-#include <fcntl.h>
-#endif
-
-#undef G_LOG_DOMAIN
-#define G_LOG_DOMAIN "event_pipe"
-
-static int event_pipe[2];
-static GIOChannel *event_channel;
-static guint event_pipe_source_id;
-static GMutex *event_pipe_mutex;
-static bool pipe_events[PIPE_EVENT_MAX];
-static event_pipe_callback_t event_pipe_callbacks[PIPE_EVENT_MAX];
-
-/**
- * Invoke the callback for a certain event.
- */
-static void
-event_pipe_invoke(enum pipe_event event)
-{
- assert((unsigned)event < PIPE_EVENT_MAX);
- assert(event_pipe_callbacks[event] != NULL);
-
- event_pipe_callbacks[event]();
-}
-
-static gboolean
-main_notify_event(G_GNUC_UNUSED GIOChannel *source,
- G_GNUC_UNUSED GIOCondition condition,
- G_GNUC_UNUSED gpointer data)
-{
- char buffer[256];
- gsize bytes_read;
- GError *error = NULL;
- GIOStatus status = g_io_channel_read_chars(event_channel,
- buffer, sizeof(buffer),
- &bytes_read, &error);
- if (status == G_IO_STATUS_ERROR)
- MPD_ERROR("error reading from pipe: %s", error->message);
-
- bool events[PIPE_EVENT_MAX];
- g_mutex_lock(event_pipe_mutex);
- memcpy(events, pipe_events, sizeof(events));
- memset(pipe_events, 0, sizeof(pipe_events));
- g_mutex_unlock(event_pipe_mutex);
-
- for (unsigned i = 0; i < PIPE_EVENT_MAX; ++i)
- if (events[i])
- /* invoke the event handler */
- event_pipe_invoke(i);
-
- return true;
-}
-
-void event_pipe_init(void)
-{
- GIOChannel *channel;
- int ret;
-
- ret = pipe_cloexec_nonblock(event_pipe);
- if (ret < 0)
- MPD_ERROR("Couldn't open pipe: %s", strerror(errno));
-
-#ifndef G_OS_WIN32
- channel = g_io_channel_unix_new(event_pipe[0]);
-#else
- channel = g_io_channel_win32_new_fd(event_pipe[0]);
-#endif
- g_io_channel_set_encoding(channel, NULL, NULL);
- g_io_channel_set_buffered(channel, false);
-
- event_pipe_source_id = g_io_add_watch(channel, G_IO_IN,
- main_notify_event, NULL);
-
- event_channel = channel;
-
- event_pipe_mutex = g_mutex_new();
-}
-
-void event_pipe_deinit(void)
-{
- g_mutex_free(event_pipe_mutex);
-
- g_source_remove(event_pipe_source_id);
- g_io_channel_unref(event_channel);
-
-#ifndef WIN32
- /* By some strange reason this call hangs on Win32 */
- close(event_pipe[0]);
-#endif
- close(event_pipe[1]);
-}
-
-void
-event_pipe_register(enum pipe_event event, event_pipe_callback_t callback)
-{
- assert((unsigned)event < PIPE_EVENT_MAX);
- assert(event_pipe_callbacks[event] == NULL);
-
- event_pipe_callbacks[event] = callback;
-}
-
-void event_pipe_emit(enum pipe_event event)
-{
- ssize_t w;
-
- assert((unsigned)event < PIPE_EVENT_MAX);
-
- g_mutex_lock(event_pipe_mutex);
- if (pipe_events[event]) {
- /* already set: don't write */
- g_mutex_unlock(event_pipe_mutex);
- return;
- }
-
- pipe_events[event] = true;
- g_mutex_unlock(event_pipe_mutex);
-
- w = write(event_pipe[1], "", 1);
- if (w < 0 && errno != EAGAIN && errno != EINTR)
- MPD_ERROR("error writing to pipe: %s", strerror(errno));
-}
-
-void event_pipe_emit_fast(enum pipe_event event)
-{
- assert((unsigned)event < PIPE_EVENT_MAX);
-
- pipe_events[event] = true;
-
- G_GNUC_UNUSED ssize_t nbytes = write(event_pipe[1], "", 1);
-}
diff --git a/src/event_pipe.h b/src/event_pipe.h
deleted file mode 100644
index 3734bb86c..000000000
--- a/src/event_pipe.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2003-2011 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 EVENT_PIPE_H
-#define EVENT_PIPE_H
-
-#include <glib.h>
-
-enum pipe_event {
- /** database update was finished */
- PIPE_EVENT_UPDATE,
-
- /** during database update, a song was deleted */
- PIPE_EVENT_DELETE,
-
- /** an idle event was emitted */
- PIPE_EVENT_IDLE,
-
- /** must call playlist_sync() */
- PIPE_EVENT_PLAYLIST,
-
- /** the current song's tag has changed */
- PIPE_EVENT_TAG,
-
- /** SIGHUP received: reload configuration, roll log file */
- PIPE_EVENT_RELOAD,
-
- /** a hardware mixer plugin has detected a change */
- PIPE_EVENT_MIXER,
-
- /** shutdown requested */
- PIPE_EVENT_SHUTDOWN,
-
- PIPE_EVENT_MAX
-};
-
-typedef void (*event_pipe_callback_t)(void);
-
-void event_pipe_init(void);
-
-void event_pipe_deinit(void);
-
-void
-event_pipe_register(enum pipe_event event, event_pipe_callback_t callback);
-
-void event_pipe_emit(enum pipe_event event);
-
-/**
- * Similar to event_pipe_emit(), but aimed for use in signal handlers:
- * it doesn't lock the mutex, and doesn't log on error. That makes it
- * potentially lossy, but for its intended use, that does not matter.
- */
-void event_pipe_emit_fast(enum pipe_event event);
-
-#endif /* MAIN_NOTIFY_H */