diff --git a/include/util/SafeSignal.hpp b/include/util/SafeSignal.hpp new file mode 100644 index 0000000..b2beff4 --- /dev/null +++ b/include/util/SafeSignal.hpp @@ -0,0 +1,59 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +namespace waybar { + +/** + * Thread-safe signal wrapper. + * Uses Glib::Dispatcher to pass events to another thread and locked queue to pass the arguments. + */ +template +struct SafeSignal : sigc::signal...)> { + public: + SafeSignal() { dp_.connect(sigc::mem_fun(*this, &SafeSignal::handle_event)); } + + template + void emit(EmitArgs&&... args) { + { + std::unique_lock lock(mutex_); + queue_.emplace(std::forward(args)...); + } + dp_.emit(); + } + + template + inline void operator()(EmitArgs&&... args) { + emit(std::forward(args)...); + } + + protected: + using signal_t = sigc::signal...)>; + using arg_tuple_t = std::tuple...>; + // ensure that unwrapped methods are not accessible + using signal_t::emit_reverse; + using signal_t::make_slot; + + void handle_event() { + auto fn = signal_t::make_slot(); + for (std::unique_lock lock(mutex_); !queue_.empty(); lock.lock()) { + auto args = queue_.front(); + queue_.pop(); + lock.unlock(); + std::apply(fn, args); + } + } + + Glib::Dispatcher dp_; + std::mutex mutex_; + std::queue queue_; +}; + +} // namespace waybar