mirror of
https://github.com/aristocratos/btop.git
synced 2024-09-28 22:21:35 +02:00
Fixed: Swapped from atomic bool spinlocks to mutexes to fix rare deadlock
This commit is contained in:
parent
a1bda5f30c
commit
804fe60ca9
71
src/btop.cpp
71
src/btop.cpp
@ -30,6 +30,7 @@ tab-size = 4
|
||||
#include <tuple>
|
||||
#include <regex>
|
||||
#include <chrono>
|
||||
#include <mutex>
|
||||
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
@ -40,7 +41,7 @@ tab-size = 4
|
||||
#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_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 rng = std::ranges;
|
||||
using namespace Tools;
|
||||
@ -79,7 +80,6 @@ namespace Global {
|
||||
uint64_t start_time;
|
||||
|
||||
atomic<bool> resized (false);
|
||||
atomic<bool> resizing (false);
|
||||
atomic<bool> quitting (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
|
||||
void term_resize(bool force) {
|
||||
if (Global::resizing) return;
|
||||
atomic_lock lck(Global::resizing);
|
||||
static mutex resizing;
|
||||
lock_guard<mutex> lock(resizing);
|
||||
if (auto refreshed = Term::refresh(true); refreshed or force) {
|
||||
if (force and refreshed) force = false;
|
||||
}
|
||||
@ -271,6 +271,7 @@ namespace Runner {
|
||||
atomic<bool> stopping (false);
|
||||
atomic<bool> waiting (false);
|
||||
atomic<bool> redraw (false);
|
||||
// mutex runner_mtx;
|
||||
|
||||
//* Setup semaphore for triggering thread to do work
|
||||
#if __GNUC__ < 11
|
||||
@ -302,6 +303,7 @@ namespace Runner {
|
||||
sigset_t mask;
|
||||
pthread_t runner_id;
|
||||
pthread_mutex_t mtx;
|
||||
pthread_mutex_t active_mtx;
|
||||
|
||||
const unordered_flat_map<string, uint_fast8_t> box_bits = {
|
||||
{"proc", 0b0000'0001},
|
||||
@ -387,6 +389,7 @@ namespace Runner {
|
||||
//* ----------------------------------------------- THREAD LOOP -----------------------------------------------
|
||||
while (not Global::quitting) {
|
||||
thread_wait();
|
||||
thread_lock lock(active_mtx);
|
||||
if (stopping or Global::resized) {
|
||||
continue;
|
||||
}
|
||||
@ -589,34 +592,40 @@ namespace Runner {
|
||||
|
||||
//* 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) {
|
||||
if (active) atomic_wait(active);
|
||||
if (stopping or Global::resized) return;
|
||||
bool trigger = false;
|
||||
{
|
||||
thread_lock lock(active_mtx);
|
||||
if (stopping or Global::resized) return;
|
||||
|
||||
if (box == "overlay") {
|
||||
cout << Term::sync_start << Global::overlay << Term::sync_end << flush;
|
||||
}
|
||||
else if (box == "clock") {
|
||||
cout << Term::sync_start << Global::clock << Term::sync_end << flush;
|
||||
}
|
||||
else {
|
||||
Config::unlock();
|
||||
Config::lock();
|
||||
|
||||
//? Setup bitmask for selected boxes and pass to _runner thread
|
||||
bitset<8> box_mask;
|
||||
for (const auto& box : (box == "all" ? Config::current_boxes : vector{box})) {
|
||||
box_mask |= box_bits.at(box);
|
||||
if (box == "overlay") {
|
||||
cout << Term::sync_start << Global::overlay << Term::sync_end << flush;
|
||||
}
|
||||
else if (box == "clock") {
|
||||
cout << Term::sync_start << Global::clock << Term::sync_end << flush;
|
||||
}
|
||||
else {
|
||||
Config::unlock();
|
||||
Config::lock();
|
||||
|
||||
current_conf = {box_mask, no_update, force_redraw, (not Config::getB("tty_mode") and Config::getB("background_update")), Global::overlay, Global::clock};
|
||||
//? Setup bitmask for selected boxes and pass to _runner thread
|
||||
bitset<8> box_mask;
|
||||
for (const auto& box : (box == "all" ? Config::current_boxes : vector{box})) {
|
||||
box_mask |= box_bits.at(box);
|
||||
}
|
||||
|
||||
if (Menu::active and not current_conf.background_update) Global::overlay.clear();
|
||||
current_conf = {box_mask, no_update, force_redraw, (not Config::getB("tty_mode") and Config::getB("background_update")), Global::overlay, Global::clock};
|
||||
|
||||
thread_trigger();
|
||||
if (Menu::active and not current_conf.background_update) Global::overlay.clear();
|
||||
|
||||
//? Wait for _runner thread to be active before returning
|
||||
for (int i = 0; not active and i < 10; i++) sleep_ms(1);
|
||||
trigger = true;
|
||||
}
|
||||
}
|
||||
//? 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
|
||||
@ -629,9 +638,15 @@ namespace Runner {
|
||||
exit(1);
|
||||
}
|
||||
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();
|
||||
sleep_ms(1);
|
||||
atomic_wait_for(active, false, 100);
|
||||
atomic_wait_for(active, true, 100);
|
||||
}
|
||||
stopping = false;
|
||||
}
|
||||
@ -857,7 +872,7 @@ int main(int argc, char **argv) {
|
||||
Global::resized = false;
|
||||
if (Menu::active) Menu::process();
|
||||
else Runner::run("all", true, true);
|
||||
atomic_wait(Runner::active);
|
||||
atomic_wait_for(Runner::active, true, 1000);
|
||||
}
|
||||
|
||||
//? Update clock if needed
|
||||
|
@ -21,12 +21,13 @@ tab-size = 4
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
#include <string_view>
|
||||
#include <mutex>
|
||||
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_shared.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 rng = std::ranges;
|
||||
using namespace Tools;
|
||||
@ -35,7 +36,7 @@ using namespace Tools;
|
||||
namespace Config {
|
||||
|
||||
atomic<bool> locked (false);
|
||||
atomic<bool> writelock (false);
|
||||
mutex writelock;
|
||||
bool write_new;
|
||||
|
||||
const vector<array<string, 2>> descriptions = {
|
||||
@ -256,7 +257,7 @@ namespace Config {
|
||||
unordered_flat_map<string, int> intsTmp;
|
||||
|
||||
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())
|
||||
write_new = true;
|
||||
return locked.load();
|
||||
@ -334,7 +335,7 @@ namespace Config {
|
||||
}
|
||||
|
||||
void lock() {
|
||||
atomic_wait(writelock);
|
||||
lock_guard<mutex> lock(writelock);
|
||||
locked = true;
|
||||
}
|
||||
|
||||
@ -448,7 +449,7 @@ namespace Config {
|
||||
void unlock() {
|
||||
if (not locked) return;
|
||||
atomic_wait(Runner::active);
|
||||
atomic_lock lck(writelock);
|
||||
lock_guard<mutex> lock(writelock);
|
||||
try {
|
||||
if (Proc::shown) {
|
||||
ints.at("selected_pid") = Proc::selected_pid;
|
||||
|
@ -24,6 +24,7 @@ tab-size = 4
|
||||
#include <iomanip>
|
||||
#include <utility>
|
||||
#include <robin_hood.h>
|
||||
#include <mutex>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
@ -33,7 +34,8 @@ tab-size = 4
|
||||
#include <btop_shared.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 rng = std::ranges;
|
||||
|
||||
@ -321,11 +323,18 @@ namespace Tools {
|
||||
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) {
|
||||
active_locks++;
|
||||
while (not this->atom.compare_exchange_strong(this->not_true, true, std::memory_order_relaxed)) {
|
||||
busy_wait();
|
||||
}
|
||||
this->atom.store(true);
|
||||
}
|
||||
|
||||
atomic_lock::~atomic_lock() {
|
||||
@ -375,11 +384,10 @@ namespace Tools {
|
||||
|
||||
namespace Logger {
|
||||
using namespace Tools;
|
||||
namespace {
|
||||
std::atomic<bool> busy (false);
|
||||
bool first = true;
|
||||
const string tdf = "%Y/%m/%d (%T) | ";
|
||||
}
|
||||
|
||||
mutex logger_mtx;
|
||||
bool first = true;
|
||||
const string tdf = "%Y/%m/%d (%T) | ";
|
||||
|
||||
size_t loglevel;
|
||||
fs::path logfile;
|
||||
@ -390,7 +398,7 @@ namespace Logger {
|
||||
|
||||
void log_write(const size_t level, const string& msg) {
|
||||
if (loglevel < level or logfile.empty()) return;
|
||||
atomic_lock lck(busy);
|
||||
lock_guard<mutex> lock(logger_mtx);
|
||||
std::error_code ec;
|
||||
try {
|
||||
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
|
||||
|
@ -281,9 +281,9 @@ namespace Tools {
|
||||
#endif
|
||||
}
|
||||
|
||||
inline 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(const atomic<bool>& atom, const bool old=true) noexcept;
|
||||
|
||||
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
|
||||
class atomic_lock {
|
||||
|
Loading…
Reference in New Issue
Block a user