Last active
November 2, 2023 21:10
-
-
Save palacaze/40ced0819b07906609641f6df9451097 to your computer and use it in GitHub Desktop.
C++ Interrupt handler using ASIO
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <condition_variable> | |
#include <functional> | |
#include <mutex> | |
#include <thread> | |
#include <asio.hpp> | |
#include <asio/signal_set.hpp> | |
namespace system { | |
/** | |
* A Signal interrupt handler object. | |
* | |
* One should not install signal handlers using signal(). At least one thread should | |
* leave the signals to be blocked unblocked. | |
* | |
* Beware of thread safety, the handler will get called from inside a worker thread. | |
* | |
*/ | |
class InterruptHandler { | |
public: | |
/** | |
* Create an interrupt handler that will invoke a function when a signal is fired | |
* | |
* @tparam Handler the type of a callable that gets executed when a signal is fired. | |
* Handler = bool(*)(int sig); | |
* @param sig the signal that was fired | |
* @return true if we must stop interrupt handling | |
* | |
* @param handler the handler to execute | |
* @param sigs the signals to block | |
*/ | |
template <typename Handler, typename... Ints> | |
explicit InterruptHandler(Handler handler, Ints ...sigs) | |
: m_ioc{} | |
, m_sigs(m_ioc) | |
, m_fun{std::move(handler)} | |
{ | |
(static_cast<void>(m_sigs.add(sigs)), ...); | |
m_sigs.async_wait([this] (auto e, auto n) { handlerWrapper(e, n); }); | |
m_thread = std::thread([this] { | |
m_ioc.run(); | |
m_cv.notify_all(); | |
}); | |
} | |
~InterruptHandler() | |
{ | |
cancel(); | |
} | |
/// Cancel interrupt handling, this stops the backround thread | |
void cancel() | |
{ | |
m_ioc.stop(); | |
m_thread.join(); | |
} | |
[[nodiscard]] bool isRunning() const | |
{ | |
return !m_ioc.stopped(); | |
} | |
/// Wait for interrupt handling to stop | |
void wait() | |
{ | |
std::unique_lock<std::mutex> lck(m_mutex); | |
m_cv.wait(lck); | |
} | |
private: | |
void handlerWrapper(const std::error_code &error, int n) | |
{ | |
if (error || m_fun(n)) { | |
return; | |
} | |
m_sigs.async_wait([this] (auto e, auto n) { handlerWrapper(e, n); }); | |
} | |
private: | |
asio::io_context m_ioc; | |
asio::signal_set m_sigs; | |
std::function<bool(int)> m_fun; | |
std::thread m_thread; | |
std::condition_variable m_cv; | |
std::mutex m_mutex; | |
}; | |
} // namespace system |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment