|  | 
| 20 | 20 | #include <multipass/utils.h> | 
| 21 | 21 | 
 | 
| 22 | 22 | #include <grp.h> | 
|  | 23 | +#include <scope_guard.hpp> | 
| 23 | 24 | #include <sys/stat.h> | 
| 24 | 25 | #include <sys/time.h> | 
| 25 | 26 | #include <unistd.h> | 
| @@ -185,14 +186,34 @@ timespec make_timespec(std::chrono::duration<Rep, Period> duration) | 
| 185 | 186 | std::function<std::optional<int>(const std::function<bool()>&)> mp::platform::make_quit_watchdog( | 
| 186 | 187 |     const std::chrono::milliseconds& timeout) | 
| 187 | 188 | { | 
| 188 |  | -    return [sigset = make_and_block_signals({SIGQUIT, SIGTERM, SIGHUP}), | 
|  | 189 | +    return [sigset = make_and_block_signals({SIGQUIT, SIGTERM, SIGHUP, SIGUSR2}), | 
| 189 | 190 |             time = make_timespec(timeout)](const std::function<bool()>& condition) -> std::optional<int> { | 
| 190 |  | -        while (condition()) | 
|  | 191 | +        // create a timer to send SIGUSR2 which unblocks this thread | 
|  | 192 | +        sigevent sevp{}; | 
|  | 193 | +        sevp.sigev_notify = SIGEV_SIGNAL; | 
|  | 194 | +        sevp.sigev_signo = SIGUSR2; | 
|  | 195 | + | 
|  | 196 | +        timer_t timer{}; | 
|  | 197 | +        if (timer_create(CLOCK_MONOTONIC, &sevp, &timer) == -1) | 
|  | 198 | +            throw std::runtime_error(fmt::format("Could not create timer: {}", strerror(errno))); | 
|  | 199 | + | 
|  | 200 | +        // scope guard to make sure the timer is deleted once it's no longer needed | 
|  | 201 | +        const auto timer_guard = sg::make_scope_guard([timer]() noexcept { timer_delete(timer); }); | 
|  | 202 | + | 
|  | 203 | +        // set timer interval and initial time to provided timeout | 
|  | 204 | +        const itimerspec timer_interval{time, time}; | 
|  | 205 | +        if (timer_settime(timer, 0, &timer_interval, nullptr) == -1) | 
|  | 206 | +            throw std::runtime_error(fmt::format("Could not set timer: {}", strerror(errno))); | 
|  | 207 | + | 
|  | 208 | +        // wait on signals and condition | 
|  | 209 | +        int sig = SIGUSR2; | 
|  | 210 | +        while (sig == SIGUSR2 && condition()) | 
| 191 | 211 |         { | 
| 192 |  | -            if (const int sig = sigtimedwait(&sigset, nullptr, &time); sig != -1) | 
| 193 |  | -                return sig; | 
|  | 212 | +            // can't use sigtimedwait since macOS doesn't support it | 
|  | 213 | +            sigwait(&sigset, &sig); | 
| 194 | 214 |         } | 
| 195 | 215 | 
 | 
| 196 |  | -        return std::nullopt; | 
|  | 216 | +        // if sig is SIGUSR2 then we know we're exiting because condition() was false | 
|  | 217 | +        return sig == SIGUSR2 ? std::nullopt : std::make_optional(sig); | 
| 197 | 218 |     }; | 
| 198 | 219 | } | 
0 commit comments