diff --git a/include/multipass/platform_unix.h b/include/multipass/platform_unix.h index ef82893201..a53cbfc139 100644 --- a/include/multipass/platform_unix.h +++ b/include/multipass/platform_unix.h @@ -22,12 +22,25 @@ #include -namespace multipass +#include "singleton.h" + +#define MP_POSIX_SIGNAL multipass::platform::SignalWrapper::instance() + +namespace multipass::platform { -namespace platform + +class SignalWrapper : public Singleton { +public: + SignalWrapper(const PrivatePass&) noexcept; + + virtual int mask_signals(int how, const sigset_t* sigset, sigset_t* old_set = nullptr) const; + virtual int send(pthread_t target, int signal) const; + virtual int wait(const sigset_t& sigset, int& got) const; +}; + sigset_t make_sigset(const std::vector& sigs); sigset_t make_and_block_signals(const std::vector& sigs); -} // namespace platform -} + +} // namespace multipass::platform #endif // MULTIPASS_PLATFORM_UNIX_H diff --git a/src/platform/platform_unix.cpp b/src/platform/platform_unix.cpp index 1f0a44ab98..ee7343b77d 100644 --- a/src/platform/platform_unix.cpp +++ b/src/platform/platform_unix.cpp @@ -25,7 +25,7 @@ #include #include -#include +#include namespace mp = multipass; @@ -158,6 +158,25 @@ int mp::platform::symlink_attr_from(const char* path, sftp_attributes_struct* at return 0; } +mp::platform::SignalWrapper::SignalWrapper(const PrivatePass& pass) noexcept +: Singleton(pass){} + + +int mp::platform::SignalWrapper::mask_signals(int how, const sigset_t* sigset, sigset_t* old_set) const +{ + return pthread_sigmask(how, sigset, old_set); +} + +int mp::platform::SignalWrapper::send(pthread_t target, int signal) const +{ + return pthread_kill(target, signal); +} + +int mp::platform::SignalWrapper::wait(const sigset_t& sigset, int& got) const +{ + return sigwait(std::addressof(sigset), std::addressof(got)); +} + sigset_t mp::platform::make_sigset(const std::vector& sigs) { sigset_t sigset; @@ -172,7 +191,7 @@ sigset_t mp::platform::make_sigset(const std::vector& sigs) sigset_t mp::platform::make_and_block_signals(const std::vector& sigs) { auto sigset{make_sigset(sigs)}; - pthread_sigmask(SIG_BLOCK, &sigset, nullptr); + MP_POSIX_SIGNAL.mask_signals(SIG_BLOCK, &sigset, nullptr); return sigset; } @@ -188,34 +207,25 @@ std::function(const std::function&)> mp::platform::ma { return [sigset = make_and_block_signals({SIGQUIT, SIGTERM, SIGHUP, SIGUSR2}), period](const std::function& condition) -> std::optional { - std::mutex sig_mtx; - std::condition_variable sig_cv; - int sig = SIGUSR2; - - // A signal generator that triggers after `timeout` - AutoJoinThread signaler([&sig_mtx, &sig_cv, &sig, &period, signalee = pthread_self()] { - std::unique_lock lock(sig_mtx); - while (!sig_cv.wait_for(lock, period, [&sig] { return sig != SIGUSR2; })) - { - pthread_kill(signalee, SIGUSR2); - } - }); + + // create a timer to periodically send SIGUSR2 + utils::Timer signal_generator{period, [signalee = pthread_self()] { + MP_POSIX_SIGNAL.send(signalee, SIGUSR2); + }}; // wait on signals and condition int latest_signal = SIGUSR2; while (latest_signal == SIGUSR2 && condition()) { + signal_generator.start(); + // can't use sigtimedwait since macOS doesn't support it - sigwait(&sigset, &latest_signal); + MP_POSIX_SIGNAL.wait(sigset, latest_signal); } - { // set `sig` to something other than SIGUSR2 so `signaler` knows to exit - std::lock_guard lock(sig_mtx); - sig = latest_signal == SIGUSR2 ? 0 : latest_signal; - } - sig_cv.notify_all(); + signal_generator.stop(); - // if `sig` is 0 then we know we're exiting because condition() was false - return sig ? std::make_optional(sig) : std::nullopt; + // if `latest_signal` is SIGUSR2 then we know `condition()` is false + return latest_signal == SIGUSR2 ? std::nullopt : std::make_optional(latest_signal); }; }