Fixed: Swapped from atomic bool spinlocks to mutexes to fix rare deadlock

This commit is contained in:
aristocratos 2021-10-13 20:54:36 +02:00
parent a1bda5f30c
commit 804fe60ca9
4 changed files with 70 additions and 46 deletions

View file

@ -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

View file

@ -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;

View file

@ -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) {

View file

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