From 285a264aae64780f9c8fbf22f88ebdf7ee55ab86 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Mon, 28 Dec 2020 17:26:55 -0800 Subject: [PATCH] feat(util): SafeSignal class for cross-thread signals with arguments Implement a wrapper over Glib::Dispatcher that passes the arguments to the signal consumer via synchronized `std::queue`. Arguments are always passed by value and the return type of the signal is expected to be `void`. --- include/util/SafeSignal.hpp | 59 +++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 include/util/SafeSignal.hpp 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