diff options
Diffstat (limited to '')
-rw-r--r-- | src/event/SignalMonitor.cxx | 73 | ||||
-rw-r--r-- | src/system/SignalFD.cxx | 58 | ||||
-rw-r--r-- | src/system/SignalFD.hxx | 62 |
3 files changed, 192 insertions, 1 deletions
diff --git a/src/event/SignalMonitor.cxx b/src/event/SignalMonitor.cxx index 172b47f55..ee72dea72 100644 --- a/src/event/SignalMonitor.cxx +++ b/src/event/SignalMonitor.cxx @@ -27,27 +27,58 @@ #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 */ - SocketMonitor::Steal(); +#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; @@ -57,10 +88,16 @@ private: 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) { @@ -69,13 +106,20 @@ SignalCallback(int signo) 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) { @@ -83,9 +127,14 @@ x_sigaction(int signum, const struct sigaction &act) 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); @@ -99,6 +148,7 @@ SignalMonitorFinish() } std::fill_n(signal_pending, MAX_SIGNAL, false); +#endif monitor.Destruct(); } @@ -107,25 +157,46 @@ 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; } diff --git a/src/system/SignalFD.cxx b/src/system/SignalFD.cxx new file mode 100644 index 000000000..b89775dcd --- /dev/null +++ b/src/system/SignalFD.cxx @@ -0,0 +1,58 @@ +/* + * 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 USE_SIGNALFD +#include "SignalFD.hxx" +#include "fd_util.h" +#include "FatalError.hxx" + +#include <assert.h> +#include <unistd.h> +#include <sys/signalfd.h> + +void +SignalFD::Create(const sigset_t &mask) +{ + fd = ::signalfd(fd, &mask, SFD_NONBLOCK|SFD_CLOEXEC); + if (fd < 0) + FatalSystemError("signalfd() failed"); +} + +void +SignalFD::Close() +{ + if (fd >= 0) { + ::close(fd); + fd = -1; + } +} + +int +SignalFD::Read() +{ + assert(fd >= 0); + + signalfd_siginfo info; + return read(fd, &info, sizeof(info)) > 0 + ? info.ssi_signo + : -1; +} + +#endif /* USE_SIGNALFD */ diff --git a/src/system/SignalFD.hxx b/src/system/SignalFD.hxx new file mode 100644 index 000000000..7163782d1 --- /dev/null +++ b/src/system/SignalFD.hxx @@ -0,0 +1,62 @@ +/* + * 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_SIGNAL_FD_HXX +#define MPD_SIGNAL_FD_HXX + +#include "check.h" + +#include <signal.h> + +/** + * A class that wraps signalfd(). + */ +class SignalFD { + int fd; + +public: + SignalFD():fd(-1) {} + ~SignalFD() { + Close(); + } + + SignalFD(const SignalFD &other) = delete; + SignalFD &operator=(const SignalFD &other) = delete; + + /** + * Create the signalfd or update its mask. + * + * All errors are fatal. + */ + void Create(const sigset_t &mask); + void Close(); + + int Get() const { + return fd; + } + + /** + * Read the next signal from the file descriptor. Returns the + * signal number on success or -1 if there are no more + * signals. + */ + int Read(); +}; + +#endif |