aboutsummaryrefslogtreecommitdiffstats
path: root/src/event
diff options
context:
space:
mode:
Diffstat (limited to 'src/event')
-rw-r--r--src/event/MultiSocketMonitor.cxx107
-rw-r--r--src/event/MultiSocketMonitor.hxx124
2 files changed, 231 insertions, 0 deletions
diff --git a/src/event/MultiSocketMonitor.cxx b/src/event/MultiSocketMonitor.cxx
new file mode 100644
index 000000000..6f20b907c
--- /dev/null
+++ b/src/event/MultiSocketMonitor.cxx
@@ -0,0 +1,107 @@
+/*
+ * 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 "fd_util.h"
+#include "gcc.h"
+
+#include <assert.h>
+
+/**
+ * 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))) {
+ 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::Check() const
+{
+ if (CheckSockets())
+ return true;
+
+ for (const auto &i : fds)
+ if (i.revents != 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;
+}
diff --git a/src/event/MultiSocketMonitor.hxx b/src/event/MultiSocketMonitor.hxx
new file mode 100644
index 000000000..9d0e1502b
--- /dev/null
+++ b/src/event/MultiSocketMonitor.hxx
@@ -0,0 +1,124 @@
+/*
+ * 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"
+
+#include <glib.h>
+
+#include <forward_list>
+
+#include <assert.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 {
+ struct Source {
+ GSource base;
+
+ MultiSocketMonitor *monitor;
+ };
+
+ EventLoop &loop;
+ Source *source;
+ std::forward_list<GPollFD> fds;
+
+public:
+ 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;
+
+ MultiSocketMonitor(EventLoop &_loop);
+ ~MultiSocketMonitor();
+
+public:
+ gcc_pure
+ gint64 GetTime() const {
+ return g_source_get_time(&source->base);
+ }
+
+ void InvalidateSockets() {
+ /* no-op because GLib always calls the GSource's
+ "prepare" method before each poll() anyway */
+ }
+
+ void AddSocket(int fd, unsigned events) {
+ fds.push_front({fd, gushort(events), 0});
+ g_source_add_poll(&source->base, &fds.front());
+ }
+
+ 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->events != 0);
+
+ unsigned events = e(i->fd);
+ if (events != 0) {
+ i->events = events;
+ prev = i;
+ } else {
+ g_source_remove_poll(&source->base, &*i);
+ fds.erase_after(prev);
+ }
+ }
+ }
+
+protected:
+ virtual void PrepareSockets(gcc_unused gint *timeout_r) {}
+ virtual bool CheckSockets() const { return false; }
+ virtual void DispatchSockets() = 0;
+
+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) {
+ PrepareSockets(timeout_r);
+ return false;
+ }
+
+ bool Check() const;
+
+ void Dispatch() {
+ DispatchSockets();
+ }
+};
+
+#endif