diff options
author | Max Kellermann <max@musicpd.org> | 2019-12-20 13:54:16 +0100 |
---|---|---|
committer | Max Kellermann <max@musicpd.org> | 2019-12-22 12:08:44 +0100 |
commit | 9a577f8060413f7d5d2b557f7067dad4bceeeb36 (patch) | |
tree | 027046c7d5845008ef7065f1b669bbb24eb53221 /src/event/MultiSocketMonitor.cxx | |
parent | d75a0d714ec966e63f75c9e81bd82dfae10d84a0 (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
Diffstat (limited to 'src/event/MultiSocketMonitor.cxx')
-rw-r--r-- | src/event/MultiSocketMonitor.cxx | 36 |
1 files changed, 35 insertions, 1 deletions
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 |