diff options
-rw-r--r-- | Makefile.am | 7 | ||||
-rw-r--r-- | src/GlobalEvents.cxx | 38 | ||||
-rw-r--r-- | src/event/WakeFD.cxx | 68 | ||||
-rw-r--r-- | src/event/WakeFD.hxx | 69 |
4 files changed, 155 insertions, 27 deletions
diff --git a/Makefile.am b/Makefile.am index 6407f9d82..c1b76fa4d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,6 +9,7 @@ bin_PROGRAMS = src/mpd noinst_LIBRARIES = \ libutil.a \ + libevent.a \ libpcm.a \ libtag.a \ libinput.a \ @@ -39,6 +40,7 @@ src_mpd_LDADD = \ $(ENCODER_LIBS) \ $(MIXER_LIBS) \ libutil.a \ + libevent.a \ $(SYSTEMD_DAEMON_LIBS) \ $(GLIB_LIBS) @@ -348,6 +350,11 @@ libutil_a_SOURCES = \ src/util/byte_reverse.c src/util/byte_reverse.h \ src/util/bit_reverse.c src/util/bit_reverse.h +# Event loop library + +libevent_a_SOURCES = \ + src/event/WakeFD.cxx + # PCM library libpcm_a_SOURCES = \ diff --git a/src/GlobalEvents.cxx b/src/GlobalEvents.cxx index 4bc607dfb..a483e31b4 100644 --- a/src/GlobalEvents.cxx +++ b/src/GlobalEvents.cxx @@ -19,22 +19,20 @@ #include "config.h" #include "GlobalEvents.hxx" +#include "event/WakeFD.hxx" #include "thread/Mutex.hxx" -#include "fd_util.h" #include "mpd_error.h" #include <assert.h> #include <glib.h> #include <string.h> #include <errno.h> -#include <unistd.h> #undef G_LOG_DOMAIN #define G_LOG_DOMAIN "global_events" namespace GlobalEvents { - static int fds[2]; - static GIOChannel *channel; + static WakeFD wake_fd; static guint source_id; static Mutex mutex; static bool flags[MAX]; @@ -58,14 +56,8 @@ GlobalEventCallback(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(GlobalEvents::channel, - buffer, sizeof(buffer), - &bytes_read, &error); - if (status == G_IO_STATUS_ERROR) - MPD_ERROR("error reading from pipe: %s", error->message); + if (!GlobalEvents::wake_fd.Read()) + return true; bool events[GlobalEvents::MAX]; GlobalEvents::mutex.lock(); @@ -85,32 +77,26 @@ GlobalEventCallback(G_GNUC_UNUSED GIOChannel *source, void GlobalEvents::Initialize() { - if (pipe_cloexec_nonblock(fds) < 0) + if (!wake_fd.Create()) MPD_ERROR("Couldn't open pipe: %s", strerror(errno)); #ifndef G_OS_WIN32 - channel = g_io_channel_unix_new(fds[0]); + GIOChannel *channel = g_io_channel_unix_new(wake_fd.Get()); #else - channel = g_io_channel_win32_new_fd(fds[0]); + GIOChannel *channel = g_io_channel_win32_new_fd(wake_fd.Get()); #endif - g_io_channel_set_encoding(channel, NULL, NULL); - g_io_channel_set_buffered(channel, false); source_id = g_io_add_watch(channel, G_IO_IN, GlobalEventCallback, NULL); + g_io_channel_unref(channel); } void GlobalEvents::Deinitialize() { g_source_remove(source_id); - g_io_channel_unref(channel); -#ifndef WIN32 - /* By some strange reason this call hangs on Win32 */ - close(fds[0]); -#endif - close(fds[1]); + wake_fd.Destroy(); } void @@ -137,9 +123,7 @@ GlobalEvents::Emit(Event event) flags[event] = true; mutex.unlock(); - ssize_t w = write(fds[1], "", 1); - if (w < 0 && errno != EAGAIN && errno != EINTR) - MPD_ERROR("error writing to pipe: %s", strerror(errno)); + wake_fd.Write(); } void @@ -149,5 +133,5 @@ GlobalEvents::FastEmit(Event event) flags[event] = true; - G_GNUC_UNUSED ssize_t nbytes = write(fds[1], "", 1); + wake_fd.Write(); } diff --git a/src/event/WakeFD.cxx b/src/event/WakeFD.cxx new file mode 100644 index 000000000..7314342d8 --- /dev/null +++ b/src/event/WakeFD.cxx @@ -0,0 +1,68 @@ +/* + * 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 "WakeFD.hxx" +#include "fd_util.h" +#include "gcc.h" + +#include <unistd.h> + +bool +WakeFD::Create() +{ + assert(fds[0] == -1); + assert(fds[1] == -1); + + return pipe_cloexec_nonblock(fds) >= 0; +} + +void +WakeFD::Destroy() +{ +#ifndef WIN32 + /* By some strange reason this call hangs on Win32 */ + close(fds[0]); +#endif + close(fds[1]); + +#ifndef NDEBUG + fds[0] = -1; + fds[1] = -1; +#endif +} + +bool +WakeFD::Read() +{ + assert(fds[0] >= 0); + assert(fds[1] >= 0); + + char buffer[256]; + return read(fds[0], buffer, sizeof(buffer)) > 0; +} + +void +WakeFD::Write() +{ + assert(fds[0] >= 0); + assert(fds[1] >= 0); + + gcc_unused ssize_t nbytes = write(fds[1], "", 1); +} diff --git a/src/event/WakeFD.hxx b/src/event/WakeFD.hxx new file mode 100644 index 000000000..7b2d52675 --- /dev/null +++ b/src/event/WakeFD.hxx @@ -0,0 +1,69 @@ +/* + * 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> + +/** + * This class can be used to wake up an I/O event loop. + * + * For optimization purposes, this class does not have a constructor + * or a destructor. + */ +class WakeFD { + int fds[2]; + +public: +#ifdef NDEBUG + WakeFD() = default; +#else + WakeFD():fds{-1, -1} {}; +#endif + + WakeFD(const WakeFD &other) = delete; + WakeFD &operator=(const WakeFD &other) = delete; + + bool Create(); + void Destroy(); + + int Get() const { + assert(fds[0] >= 0); + assert(fds[1] >= 0); + + return fds[0]; + } + + /** + * Checks if Write() was called at least once since the last + * Read() call. + */ + bool Read(); + + /** + * Wakes up the reader. Multiple calls to this function will + * be combined to one wakeup. + */ + void Write(); +}; + +#endif /* MAIN_NOTIFY_H */ |