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{"