summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Kellermann <max@musicpd.org>2019-12-20 13:54:16 +0100
committerMax Kellermann <max@musicpd.org>2019-12-22 12:08:44 +0100
commit9a577f8060413f7d5d2b557f7067dad4bceeeb36 (patch)
tree027046c7d5845008ef7065f1b669bbb24eb53221
parentd75a0d714ec966e63f75c9e81bd82dfae10d84a0 (diff)
event/MultiSocketMonitor: add workaround for /dev/null
The ALSA "null" driver opens /dev/null and returns the file handle from snd_pcm_poll_descriptors(), but /dev/null cannot be used with epoll, the epoll_ctl() system call returns -EPERM. This means that the ALSA output hangs, eventually freezing the whole MPD process. This commit adds a workaround to the MultiSocketMonitor class which is used by the ALSA output plugin. Closes https://github.com/MusicPlayerDaemon/MPD/issues/695
-rw-r--r--NEWS2
-rw-r--r--src/event/MultiSocketMonitor.cxx36
-rw-r--r--src/event/MultiSocketMonitor.hxx20
3 files changed, 57 insertions, 1 deletions
diff --git a/NEWS b/NEWS
index 169a9610b..7804e0d20 100644
--- a/NEWS
+++ b/NEWS
@@ -1,4 +1,6 @@
ver 0.21.18 (not yet released)
+* output
+ - alsa: fix hang bug with ALSA "null" outputs
* reduce unnecessary CPU wakeups
ver 0.21.17 (2019/12/16)
diff --git a/src/event/MultiSocketMonitor.cxx b/src/event/MultiSocketMonitor.cxx
index 98d57332c..7704bd8e8 100644
--- a/src/event/MultiSocketMonitor.cxx
+++ b/src/event/MultiSocketMonitor.cxx
@@ -22,6 +22,10 @@
#include <algorithm>
+#ifdef USE_EPOLL
+#include <errno.h>
+#endif
+
#ifndef _WIN32
#include <poll.h>
#endif
@@ -37,6 +41,9 @@ MultiSocketMonitor::Reset() noexcept
assert(GetEventLoop().IsInside());
fds.clear();
+#ifdef USE_EPOLL
+ always_ready_fds.clear();
+#endif
IdleMonitor::Cancel();
timeout_event.Cancel();
ready = refresh = false;
@@ -49,6 +56,13 @@ MultiSocketMonitor::AddSocket(SocketDescriptor fd, unsigned events) noexcept
bool success = fds.front().Schedule(events);
if (!success) {
fds.pop_front();
+
+#ifdef USE_EPOLL
+ if (errno == EPERM)
+ /* not supported by epoll (e.g. "/dev/null"):
+ add it to the "always ready" list */
+ always_ready_fds.push_front({fd, events});
+#endif
}
return success;
@@ -60,6 +74,9 @@ MultiSocketMonitor::ClearSocketList() noexcept
assert(GetEventLoop().IsInside());
fds.clear();
+#ifdef USE_EPOLL
+ always_ready_fds.clear();
+#endif
}
#ifndef _WIN32
@@ -67,6 +84,10 @@ MultiSocketMonitor::ClearSocketList() noexcept
void
MultiSocketMonitor::ReplaceSocketList(pollfd *pfds, unsigned n) noexcept
{
+#ifdef USE_EPOLL
+ always_ready_fds.clear();
+#endif
+
pollfd *const end = pfds + n;
UpdateSocketList([pfds, end](SocketDescriptor fd) -> unsigned {
@@ -89,7 +110,20 @@ MultiSocketMonitor::ReplaceSocketList(pollfd *pfds, unsigned n) noexcept
void
MultiSocketMonitor::Prepare() noexcept
{
- const auto timeout = PrepareSockets();
+ auto timeout = PrepareSockets();
+
+#ifdef USE_EPOLL
+ if (!always_ready_fds.empty()) {
+ /* if there was at least one file descriptor not
+ supported by epoll, install a very short timeout
+ because we assume it's always ready */
+ constexpr std::chrono::steady_clock::duration ready_timeout =
+ std::chrono::milliseconds(1);
+ if (timeout < timeout.zero() || timeout > ready_timeout)
+ timeout = ready_timeout;
+ }
+#endif
+
if (timeout >= timeout.zero())
timeout_event.Schedule(timeout);
else
diff --git a/src/event/MultiSocketMonitor.hxx b/src/event/MultiSocketMonitor.hxx
index 11308d505..04f3bee8a 100644
--- a/src/event/MultiSocketMonitor.hxx
+++ b/src/event/MultiSocketMonitor.hxx
@@ -102,6 +102,21 @@ class MultiSocketMonitor : IdleMonitor
std::forward_list<SingleFD> fds;
+#ifdef USE_EPOLL
+ struct AlwaysReady {
+ const SocketDescriptor fd;
+ const unsigned revents;
+ };
+
+ /**
+ * A list of file descriptors which are always ready. This is
+ * a kludge needed because the ALSA output plugin gives us a
+ * file descriptor to /dev/null, which is incompatible with
+ * epoll (epoll_ctl() returns -EPERM).
+ */
+ std::forward_list<AlwaysReady> always_ready_fds;
+#endif
+
public:
static constexpr unsigned READ = SocketMonitor::READ;
static constexpr unsigned WRITE = SocketMonitor::WRITE;
@@ -198,6 +213,11 @@ public:
i.ClearReturnedEvents();
}
}
+
+#ifdef USE_EPOLL
+ for (const auto &i : always_ready_fds)
+ f(i.fd, i.revents);
+#endif
}
protected: