mirror of https://github.com/aristocratos/btop.git
Fixed: Swapped from atomic bool spinlocks to mutexes to fix rare deadlock
This commit is contained in:
parent
a1bda5f30c
commit
804fe60ca9
39
src/btop.cpp
39
src/btop.cpp
|
@ -30,6 +30,7 @@ tab-size = 4
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <btop_shared.hpp>
|
#include <btop_shared.hpp>
|
||||||
#include <btop_tools.hpp>
|
#include <btop_tools.hpp>
|
||||||
|
@ -40,7 +41,7 @@ tab-size = 4
|
||||||
#include <btop_menu.hpp>
|
#include <btop_menu.hpp>
|
||||||
|
|
||||||
using std::string, std::string_view, std::vector, std::atomic, std::endl, std::cout, std::min, std::flush, std::endl;
|
using std::string, std::string_view, std::vector, std::atomic, std::endl, std::cout, std::min, std::flush, std::endl;
|
||||||
using std::string_literals::operator""s, std::to_string, std::future, std::async, std::bitset, std::future_status;
|
using std::string_literals::operator""s, std::to_string, std::future, std::async, std::bitset, std::future_status, std::mutex, std::lock_guard;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
namespace rng = std::ranges;
|
namespace rng = std::ranges;
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
|
@ -79,7 +80,6 @@ namespace Global {
|
||||||
uint64_t start_time;
|
uint64_t start_time;
|
||||||
|
|
||||||
atomic<bool> resized (false);
|
atomic<bool> resized (false);
|
||||||
atomic<bool> resizing (false);
|
|
||||||
atomic<bool> quitting (false);
|
atomic<bool> quitting (false);
|
||||||
atomic<bool> _runner_started (false);
|
atomic<bool> _runner_started (false);
|
||||||
|
|
||||||
|
@ -150,8 +150,8 @@ void argumentParser(const int& argc, char **argv) {
|
||||||
|
|
||||||
//* Handler for SIGWINCH and general resizing events, does nothing if terminal hasn't been resized unless force=true
|
//* Handler for SIGWINCH and general resizing events, does nothing if terminal hasn't been resized unless force=true
|
||||||
void term_resize(bool force) {
|
void term_resize(bool force) {
|
||||||
if (Global::resizing) return;
|
static mutex resizing;
|
||||||
atomic_lock lck(Global::resizing);
|
lock_guard<mutex> lock(resizing);
|
||||||
if (auto refreshed = Term::refresh(true); refreshed or force) {
|
if (auto refreshed = Term::refresh(true); refreshed or force) {
|
||||||
if (force and refreshed) force = false;
|
if (force and refreshed) force = false;
|
||||||
}
|
}
|
||||||
|
@ -271,6 +271,7 @@ namespace Runner {
|
||||||
atomic<bool> stopping (false);
|
atomic<bool> stopping (false);
|
||||||
atomic<bool> waiting (false);
|
atomic<bool> waiting (false);
|
||||||
atomic<bool> redraw (false);
|
atomic<bool> redraw (false);
|
||||||
|
// mutex runner_mtx;
|
||||||
|
|
||||||
//* Setup semaphore for triggering thread to do work
|
//* Setup semaphore for triggering thread to do work
|
||||||
#if __GNUC__ < 11
|
#if __GNUC__ < 11
|
||||||
|
@ -302,6 +303,7 @@ namespace Runner {
|
||||||
sigset_t mask;
|
sigset_t mask;
|
||||||
pthread_t runner_id;
|
pthread_t runner_id;
|
||||||
pthread_mutex_t mtx;
|
pthread_mutex_t mtx;
|
||||||
|
pthread_mutex_t active_mtx;
|
||||||
|
|
||||||
const unordered_flat_map<string, uint_fast8_t> box_bits = {
|
const unordered_flat_map<string, uint_fast8_t> box_bits = {
|
||||||
{"proc", 0b0000'0001},
|
{"proc", 0b0000'0001},
|
||||||
|
@ -387,6 +389,7 @@ namespace Runner {
|
||||||
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
|
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
|
||||||
while (not Global::quitting) {
|
while (not Global::quitting) {
|
||||||
thread_wait();
|
thread_wait();
|
||||||
|
thread_lock lock(active_mtx);
|
||||||
if (stopping or Global::resized) {
|
if (stopping or Global::resized) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -589,7 +592,9 @@ namespace Runner {
|
||||||
|
|
||||||
//* Runs collect and draw in a secondary thread, unlocks and locks config to update cached values
|
//* Runs collect and draw in a secondary thread, unlocks and locks config to update cached values
|
||||||
void run(const string& box, const bool no_update, const bool force_redraw) {
|
void run(const string& box, const bool no_update, const bool force_redraw) {
|
||||||
if (active) atomic_wait(active);
|
bool trigger = false;
|
||||||
|
{
|
||||||
|
thread_lock lock(active_mtx);
|
||||||
if (stopping or Global::resized) return;
|
if (stopping or Global::resized) return;
|
||||||
|
|
||||||
if (box == "overlay") {
|
if (box == "overlay") {
|
||||||
|
@ -612,12 +617,16 @@ namespace Runner {
|
||||||
|
|
||||||
if (Menu::active and not current_conf.background_update) Global::overlay.clear();
|
if (Menu::active and not current_conf.background_update) Global::overlay.clear();
|
||||||
|
|
||||||
thread_trigger();
|
trigger = true;
|
||||||
|
|
||||||
//? Wait for _runner thread to be active before returning
|
|
||||||
for (int i = 0; not active and i < 10; i++) sleep_ms(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//? Trigger thread start and wait for it to be active before returning
|
||||||
|
if (trigger) {
|
||||||
|
thread_trigger();
|
||||||
|
atomic_wait_for(active, false, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
//* Stops any work being done in runner thread and checks for thread errors
|
//* Stops any work being done in runner thread and checks for thread errors
|
||||||
void stop() {
|
void stop() {
|
||||||
|
@ -629,9 +638,15 @@ namespace Runner {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
else if (ret == EBUSY) {
|
else if (ret == EBUSY) {
|
||||||
atomic_wait(active);
|
atomic_wait_for(active, true, 1000);
|
||||||
|
if (active) {
|
||||||
|
Global::exit_error_msg = "No response from Runner thread, quitting!";
|
||||||
|
active = false;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
thread_trigger();
|
thread_trigger();
|
||||||
sleep_ms(1);
|
atomic_wait_for(active, false, 100);
|
||||||
|
atomic_wait_for(active, true, 100);
|
||||||
}
|
}
|
||||||
stopping = false;
|
stopping = false;
|
||||||
}
|
}
|
||||||
|
@ -857,7 +872,7 @@ int main(int argc, char **argv) {
|
||||||
Global::resized = false;
|
Global::resized = false;
|
||||||
if (Menu::active) Menu::process();
|
if (Menu::active) Menu::process();
|
||||||
else Runner::run("all", true, true);
|
else Runner::run("all", true, true);
|
||||||
atomic_wait(Runner::active);
|
atomic_wait_for(Runner::active, true, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
//? Update clock if needed
|
//? Update clock if needed
|
||||||
|
|
|
@ -21,12 +21,13 @@ tab-size = 4
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <btop_config.hpp>
|
#include <btop_config.hpp>
|
||||||
#include <btop_shared.hpp>
|
#include <btop_shared.hpp>
|
||||||
#include <btop_tools.hpp>
|
#include <btop_tools.hpp>
|
||||||
|
|
||||||
using std::array, std::atomic, std::string_view, std::string_literals::operator""s;
|
using std::array, std::atomic, std::string_view, std::string_literals::operator""s, std::mutex, std::lock_guard;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
namespace rng = std::ranges;
|
namespace rng = std::ranges;
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
|
@ -35,7 +36,7 @@ using namespace Tools;
|
||||||
namespace Config {
|
namespace Config {
|
||||||
|
|
||||||
atomic<bool> locked (false);
|
atomic<bool> locked (false);
|
||||||
atomic<bool> writelock (false);
|
mutex writelock;
|
||||||
bool write_new;
|
bool write_new;
|
||||||
|
|
||||||
const vector<array<string, 2>> descriptions = {
|
const vector<array<string, 2>> descriptions = {
|
||||||
|
@ -256,7 +257,7 @@ namespace Config {
|
||||||
unordered_flat_map<string, int> intsTmp;
|
unordered_flat_map<string, int> intsTmp;
|
||||||
|
|
||||||
bool _locked(const string& name) {
|
bool _locked(const string& name) {
|
||||||
atomic_wait(writelock);
|
lock_guard<mutex> lock(writelock);
|
||||||
if (not write_new and rng::find_if(descriptions, [&name](const auto& a) { return a.at(0) == name; }) != descriptions.end())
|
if (not write_new and rng::find_if(descriptions, [&name](const auto& a) { return a.at(0) == name; }) != descriptions.end())
|
||||||
write_new = true;
|
write_new = true;
|
||||||
return locked.load();
|
return locked.load();
|
||||||
|
@ -334,7 +335,7 @@ namespace Config {
|
||||||
}
|
}
|
||||||
|
|
||||||
void lock() {
|
void lock() {
|
||||||
atomic_wait(writelock);
|
lock_guard<mutex> lock(writelock);
|
||||||
locked = true;
|
locked = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,7 +449,7 @@ namespace Config {
|
||||||
void unlock() {
|
void unlock() {
|
||||||
if (not locked) return;
|
if (not locked) return;
|
||||||
atomic_wait(Runner::active);
|
atomic_wait(Runner::active);
|
||||||
atomic_lock lck(writelock);
|
lock_guard<mutex> lock(writelock);
|
||||||
try {
|
try {
|
||||||
if (Proc::shown) {
|
if (Proc::shown) {
|
||||||
ints.at("selected_pid") = Proc::selected_pid;
|
ints.at("selected_pid") = Proc::selected_pid;
|
||||||
|
|
|
@ -24,6 +24,7 @@ tab-size = 4
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <robin_hood.h>
|
#include <robin_hood.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
@ -33,7 +34,8 @@ tab-size = 4
|
||||||
#include <btop_shared.hpp>
|
#include <btop_shared.hpp>
|
||||||
#include <btop_tools.hpp>
|
#include <btop_tools.hpp>
|
||||||
|
|
||||||
using std::string_view, std::max, std::floor, std::to_string, std::cin, std::cout, std::flush, robin_hood::unordered_flat_map;
|
using std::string_view, std::max, std::floor, std::to_string, std::cin, std::cout, std::flush, robin_hood::unordered_flat_map,
|
||||||
|
std::mutex, std::lock_guard;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
namespace rng = std::ranges;
|
namespace rng = std::ranges;
|
||||||
|
|
||||||
|
@ -321,11 +323,18 @@ namespace Tools {
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void atomic_wait(const atomic<bool>& atom, const bool old) noexcept {
|
||||||
|
while (atom.load(std::memory_order_relaxed) == old ) busy_wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void atomic_wait_for(const atomic<bool>& atom, const bool old, const uint64_t wait_ms) noexcept {
|
||||||
|
const uint64_t start_time = time_ms();
|
||||||
|
while (atom.load(std::memory_order_relaxed) == old and (time_ms() - start_time < wait_ms)) sleep_ms(1);
|
||||||
|
}
|
||||||
|
|
||||||
atomic_lock::atomic_lock(atomic<bool>& atom) : atom(atom) {
|
atomic_lock::atomic_lock(atomic<bool>& atom) : atom(atom) {
|
||||||
active_locks++;
|
active_locks++;
|
||||||
while (not this->atom.compare_exchange_strong(this->not_true, true, std::memory_order_relaxed)) {
|
this->atom.store(true);
|
||||||
busy_wait();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic_lock::~atomic_lock() {
|
atomic_lock::~atomic_lock() {
|
||||||
|
@ -375,11 +384,10 @@ namespace Tools {
|
||||||
|
|
||||||
namespace Logger {
|
namespace Logger {
|
||||||
using namespace Tools;
|
using namespace Tools;
|
||||||
namespace {
|
|
||||||
std::atomic<bool> busy (false);
|
mutex logger_mtx;
|
||||||
bool first = true;
|
bool first = true;
|
||||||
const string tdf = "%Y/%m/%d (%T) | ";
|
const string tdf = "%Y/%m/%d (%T) | ";
|
||||||
}
|
|
||||||
|
|
||||||
size_t loglevel;
|
size_t loglevel;
|
||||||
fs::path logfile;
|
fs::path logfile;
|
||||||
|
@ -390,7 +398,7 @@ namespace Logger {
|
||||||
|
|
||||||
void log_write(const size_t level, const string& msg) {
|
void log_write(const size_t level, const string& msg) {
|
||||||
if (loglevel < level or logfile.empty()) return;
|
if (loglevel < level or logfile.empty()) return;
|
||||||
atomic_lock lck(busy);
|
lock_guard<mutex> lock(logger_mtx);
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
try {
|
try {
|
||||||
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
|
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
|
||||||
|
|
|
@ -281,9 +281,9 @@ namespace Tools {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept {
|
void atomic_wait(const atomic<bool>& atom, const bool old=true) noexcept;
|
||||||
while (atom.load(std::memory_order_relaxed) == old) busy_wait();
|
|
||||||
}
|
void atomic_wait_for(const atomic<bool>& atom, const bool old=true, const uint64_t wait_ms=0) noexcept;
|
||||||
|
|
||||||
//* Waits for atomic<bool> to be false and sets it to true on construct, sets to false on destruct
|
//* Waits for atomic<bool> to be false and sets it to true on construct, sets to false on destruct
|
||||||
class atomic_lock {
|
class atomic_lock {
|
||||||
|
|
Loading…
Reference in New Issue