From 444120e664b68761e5a024ebfdd710ff65872476 Mon Sep 17 00:00:00 2001 From: Patrick Ziegler Date: Sun, 3 Oct 2021 01:27:11 +0200 Subject: [PATCH] script: Fix concurrency issues (#2518) Fixes #1978 * Move tail and non-tail handler to method Defining them in the constructor is ugly. * script: Iterate over defined actions instead of fixed list * Separate running logic and lock m_output * Include POLYBAR_FLAGS in linker flags * Stop using m_prev in script_runner * Join module threads in stop function Joining in the destructor may lead to UB because the subclass is already deconstructed but the threads may still require it to be around (e.g. for calling any functions on the instance) * Cleanup script module * Update changelog * Remove AfterReturn class * Remove m_stopping from script module * Fix polybar not reading the entire line from child process. For every `readline` call we created a new fd_streambuf. This means once `readline` returns, the streambuf is destructed and and pending data in its temporary buffer discarded and we never actually read it. * Remove unused includes --- CHANGELOG.md | 2 + cmake/cxx.cmake | 4 +- include/adapters/script_runner.hpp | 57 +++++++++++ include/components/eventloop.hpp | 2 +- include/modules/meta/base.inl | 32 +++--- include/modules/script.hpp | 21 +--- include/utils/command.hpp | 25 ++--- include/utils/file.hpp | 11 ++- include/utils/io.hpp | 15 +-- src/CMakeLists.txt | 2 + src/adapters/script_runner.cpp | 154 +++++++++++++++++++++++++++++ src/modules/script.cpp | 154 ++++++----------------------- src/utils/command.cpp | 26 +++-- src/utils/file.cpp | 21 ++-- src/utils/io.cpp | 65 +----------- tests/unit_tests/utils/command.cpp | 14 +-- 16 files changed, 316 insertions(+), 289 deletions(-) create mode 100644 include/adapters/script_runner.hpp create mode 100644 src/adapters/script_runner.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d03cedc..04063b28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -143,6 +143,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Increased the double click interval from 150ms to 400ms. ### Fixed +- `custom/script`: Concurrency issues with fast-updating tailed scripts. + ([`#1978`](https://github.com/polybar/polybar/issues/1978)) - Trailing space after the layout label when indicators are empty and made sure right amount of spacing is added between the indicator labels, in the xkeyboard module. ([`#2292`](https://github.com/polybar/polybar/issues/2292)) diff --git a/cmake/cxx.cmake b/cmake/cxx.cmake index 128b12ca..fa6776af 100644 --- a/cmake/cxx.cmake +++ b/cmake/cxx.cmake @@ -99,11 +99,11 @@ elseif (CMAKE_BUILD_TYPE_UPPER STREQUAL "COVERAGE") list(APPEND cxx_flags ${cxx_coverage}) endif() -list(APPEND cxx_linker_flags ${cxx_flags}) - string(REPLACE " " ";" polybar_flags_list "${POLYBAR_FLAGS}") list(APPEND cxx_flags ${polybar_flags_list}) +list(APPEND cxx_linker_flags ${cxx_flags}) + string(REPLACE ";" " " cxx_flags_str "${cxx_flags}") string(REPLACE ";" " " cxx_linker_flags_str "${cxx_linker_flags}") diff --git a/include/adapters/script_runner.hpp b/include/adapters/script_runner.hpp new file mode 100644 index 00000000..9ecec1c1 --- /dev/null +++ b/include/adapters/script_runner.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include + +#include "common.hpp" +#include "components/logger.hpp" + +POLYBAR_NS + +class script_runner { + public: + using interval = std::chrono::duration; + script_runner(std::function on_update, const string& exec, const string& exec_if, bool tail, + interval interval, const vector>& env); + + bool check_condition() const; + interval process(); + + void clear_output(); + + void stop(); + + int get_pid() const; + int get_counter() const; + + string get_output(); + + bool is_stopping() const; + + protected: + bool set_output(const string&&); + + interval run_tail(); + interval run(); + + private: + const logger& m_log; + + const std::function m_on_update; + + const string m_exec; + const string m_exec_if; + const bool m_tail; + const interval m_interval; + const vector> m_env; + + std::mutex m_output_lock; + string m_output; + + std::atomic_int m_counter{0}; + std::atomic_bool m_stopping{false}; + std::atomic_int m_pid{-1}; +}; + +POLYBAR_NS_END diff --git a/include/components/eventloop.hpp b/include/components/eventloop.hpp index 26f030be..553bdff4 100644 --- a/include/components/eventloop.hpp +++ b/include/components/eventloop.hpp @@ -70,7 +70,7 @@ struct UVHandleGeneric { return unpackedThis->func(std::forward(args)...); } - H* handle; + H* handle{nullptr}; function func; }; diff --git a/include/modules/meta/base.inl b/include/modules/meta/base.inl index c7742744..182a2ab2 100644 --- a/include/modules/meta/base.inl +++ b/include/modules/meta/base.inl @@ -20,6 +20,8 @@ namespace modules { , m_bar(bar) , m_log(logger::make()) , m_conf(config::make()) + // TODO this cast is illegal because 'this' is not yet of type Impl but only of type module + // Change action router to use lambdas , m_router(make_unique>(CAST_MOD(Impl))) , m_name("module/" + name) , m_name_raw(name) @@ -27,22 +29,21 @@ namespace modules { , m_formatter(make_unique(m_conf, m_name)) , m_handle_events(m_conf.get(m_name, "handle-events", true)) , m_visible(!m_conf.get(m_name, "hidden", false)) { - m_router->register_action(EVENT_MODULE_TOGGLE, &module::action_module_toggle); - m_router->register_action(EVENT_MODULE_SHOW, &module::action_module_show); - m_router->register_action(EVENT_MODULE_HIDE, &module::action_module_hide); - } + m_router->register_action(EVENT_MODULE_TOGGLE, &module::action_module_toggle); + m_router->register_action(EVENT_MODULE_SHOW, &module::action_module_show); + m_router->register_action(EVENT_MODULE_HIDE, &module::action_module_hide); + } template module::~module() noexcept { m_log.trace("%s: Deconstructing", name()); - for (auto&& thread_ : m_threads) { - if (thread_.joinable()) { - thread_.join(); - } - } - if (m_mainthread.joinable()) { - m_mainthread.join(); + if (running()) { + /* + * We can't stop in the destructor because we have to call the subclasses which at this point already have been + * destructed. + */ + m_log.err("%s: Module was not stopped before deconstructing.", name()); } } @@ -87,6 +88,15 @@ namespace modules { CAST_MOD(Impl)->wakeup(); CAST_MOD(Impl)->teardown(); + for (auto&& thread_ : m_threads) { + if (thread_.joinable()) { + thread_.join(); + } + } + if (m_mainthread.joinable()) { + m_mainthread.join(); + } + m_sig.emit(signals::eventqueue::check_state{}); } } diff --git a/include/modules/script.hpp b/include/modules/script.hpp index 0ce8bea1..c00dcdbf 100644 --- a/include/modules/script.hpp +++ b/include/modules/script.hpp @@ -1,5 +1,6 @@ #pragma once +#include "adapters/script_runner.hpp" #include "modules/meta/base.hpp" #include "utils/command.hpp" #include "utils/io.hpp" @@ -10,7 +11,6 @@ namespace modules { class script_module : public module { public: explicit script_module(const bar_settings&, string); - ~script_module() {} void start() override; void stop() override; @@ -21,32 +21,19 @@ namespace modules { static constexpr auto TYPE = "custom/script"; protected: - chrono::duration process(const mutex_wrapper()>>& handler) const; bool check_condition(); private: static constexpr const char* TAG_LABEL{"