From deec8f20e02ca9f45daa45c0b514efc62b23a877 Mon Sep 17 00:00:00 2001 From: aristocratos Date: Sun, 6 Jun 2021 01:41:36 +0200 Subject: [PATCH] Config thread safety --- README.md | 6 +++-- btop.cpp | 14 +++++----- src/btop_config.h | 64 ++++++++++++++++++++++++++++++++++++++------- src/btop_draw.h | 2 +- src/btop_input.h | 2 +- src/btop_linux.h | 2 +- src/btop_theme.h | 4 +-- src/btop_tools.h | 66 +++++++++++++++++++++++++++++++---------------- 8 files changed, 116 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 9649812..50e8026 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,9 @@ Look to the creators of the terminal emulator you use to fix these issues if the None -But will need G++ 11 if compiling from source. +### Compiling from source + +Needs at least G++ 10, preferably 11 (or higher) to make use of some C++20 functionality. ## Screenshots @@ -132,7 +134,7 @@ Options menu. #### Manual compilation and installation -Requires GCC/G++ 11! +Requires GCC/G++ 10 or higher! >Clone and compile diff --git a/btop.cpp b/btop.cpp index b7c428f..20c62c5 100644 --- a/btop.cpp +++ b/btop.cpp @@ -179,6 +179,7 @@ int main(int argc, char **argv){ std::atexit(_exit_handler); + //? Linux init #if defined(LINUX) Global::coreCount = sysconf(_SC_NPROCESSORS_ONLN); if (Global::coreCount < 1) Global::coreCount = 1; @@ -225,17 +226,17 @@ int main(int argc, char **argv){ if (Global::debug) { Logger::loglevel = 4; Logger::debug("Starting in debug mode");} if (!string(getenv("LANG")).ends_with("UTF-8") && !string(getenv("LANG")).ends_with("utf-8")) { - string err_msg = "No UTF-8 locale was detected! Symbols might not look as intended."; + string err_msg = "No UTF-8 locale was detected! Symbols might not look as intended.\n"s + + "Make sure your $LANG evironment variable is set and with a UTF-8 locale."s; Logger::warning(err_msg); cout << "WARNING: " << err_msg << endl; } //? Initialize terminal and set options if (!Term::init()) { - string err_msg = "No tty detected!"; - Logger::error(err_msg + " Quitting."); + string err_msg = "No tty detected!\nbtop++ needs an interactive shell to run."; + Logger::error(err_msg); cout << "ERROR: " << err_msg << endl; - cout << "btop++ needs an interactive shell to run." << endl; clean_quit(1); } @@ -246,7 +247,7 @@ int main(int argc, char **argv){ //? Read config file if present Config::load(); - // Config::setB("truecolor", false); + // Config::set("truecolor", false); auto thts = time_ms(); @@ -311,6 +312,7 @@ int main(int argc, char **argv){ exit(0); } + if (false) { Draw::Meter kmeter; kmeter(Term::width - 2, "cpu", false); @@ -336,7 +338,7 @@ int main(int argc, char **argv){ exit(0); } - if (true) { + if (false) { vector mydata; for (long long i = 0; i <= 100; i++) mydata.push_back(i); diff --git a/src/btop_config.h b/src/btop_config.h index 86f50a4..e1463b0 100644 --- a/src/btop_config.h +++ b/src/btop_config.h @@ -17,7 +17,7 @@ tab-size = 4 */ #ifndef _btop_config_included_ -#define _btop_config_included_ 1 +#define _btop_config_included_ #include #include @@ -38,6 +38,8 @@ namespace Config { fs::path conf_dir; fs::path conf_file; + atomic locked (false); + atomic writelock (false); bool changed = false; unordered_flat_map strings = { @@ -57,6 +59,8 @@ namespace Config { {"net_iface", ""}, {"log_level", "WARNING"} }; + unordered_flat_map stringsTmp; + unordered_flat_map bools = { {"theme_background", true}, {"truecolor", true}, @@ -89,11 +93,20 @@ namespace Config { {"show_battery", true}, {"show_init", false} }; + unordered_flat_map boolsTmp; + unordered_flat_map ints = { {"update_ms", 2000}, {"proc_update_mult", 2}, {"tree_depth", 3} }; + unordered_flat_map intsTmp; + + bool _locked(){ + atomic_wait(writelock); + if (!changed) changed = true; + return locked.load(); + } } //* Return config value as a bool @@ -113,26 +126,59 @@ namespace Config { //* Set config value to bool void set(string name, bool value){ - bools.at(name) = value; - changed = true; + if (_locked()) boolsTmp[name] = value; + else bools.at(name) = value; } //* Set config value to int void set(string name, int value){ + if (_locked()) intsTmp[name] = value; ints.at(name) = value; - changed = true; } //* Set config value to string void set(string name, string value){ - strings.at(name) = value; - changed = true; + if (_locked()) stringsTmp[name] = value; + else strings.at(name) = value; } - //* Flip config bool value + //* Flip config bool void flip(string name){ - bools.at(name) = !bools.at(name); - changed = true; + if (_locked()) { + if (boolsTmp.contains(name)) boolsTmp.at(name) = !boolsTmp.at(name); + else boolsTmp[name] = !bools.at(name); + } + else bools.at(name) = !bools.at(name); + } + + //* Wait if locked then lock config and cache changes until unlock + void lock(){ + atomic_wait_set(locked, true); + } + + //* Unlock config and write any cached values to config + void unlock(){ + atomic_wait_set(writelock, true); + if (stringsTmp.size() > 0) { + for (auto& item : stringsTmp){ + strings.at(item.first) = item.second; + } + stringsTmp.clear(); stringsTmp.compact(); + } + if (intsTmp.size() > 0) { + for (auto& item : intsTmp){ + ints.at(item.first) = item.second; + } + intsTmp.clear(); intsTmp.compact(); + } + if (boolsTmp.size() > 0) { + for (auto& item : boolsTmp){ + bools.at(item.first) = item.second; + } + boolsTmp.clear(); boolsTmp.compact(); + } + writelock.store(false); + locked.store(false); } void load(){ diff --git a/src/btop_draw.h b/src/btop_draw.h index 6736eab..029779b 100644 --- a/src/btop_draw.h +++ b/src/btop_draw.h @@ -31,7 +31,7 @@ tab-size = 4 #include #ifndef _btop_draw_included_ -#define _btop_draw_included_ 1 +#define _btop_draw_included_ using std::string, std::vector, robin_hood::unordered_flat_map, std::round, std::views::iota, std::string_literals::operator""s, std::clamp, std::array, std::floor; diff --git a/src/btop_input.h b/src/btop_input.h index 9ce1f5a..79e2fa9 100644 --- a/src/btop_input.h +++ b/src/btop_input.h @@ -17,7 +17,7 @@ tab-size = 4 */ #ifndef _btop_input_included_ -#define _btop_input_included_ 1 +#define _btop_input_included_ #include #include diff --git a/src/btop_linux.h b/src/btop_linux.h index 6c533e4..d1ce554 100644 --- a/src/btop_linux.h +++ b/src/btop_linux.h @@ -17,7 +17,7 @@ tab-size = 4 */ #ifndef _btop_linux_included_ -#define _btop_linux_included_ 1 +#define _btop_linux_included_ #include #include diff --git a/src/btop_theme.h b/src/btop_theme.h index 98c7885..900087a 100644 --- a/src/btop_theme.h +++ b/src/btop_theme.h @@ -17,7 +17,7 @@ tab-size = 4 */ #ifndef _btop_theme_included_ -#define _btop_theme_included_ 1 +#define _btop_theme_included_ #include #include @@ -240,7 +240,7 @@ namespace Theme { int rng = (rgb_arr[1][0] >= 0) ? 50 : 100; for (int rgb : iota(0, 3)){ int arr1 = 0, offset = 0; - int arr2 = (rng == 50) ? 1 : 2; + int arr2 = (rng == 50) ? 1 : 2; for (int i : iota(0, 101)) { dec_arr[i][rgb] = rgb_arr[arr1][rgb] + (i - offset) * (rgb_arr[arr2][rgb] - rgb_arr[arr1][rgb]) / rng; diff --git a/src/btop_tools.h b/src/btop_tools.h index 8bc8639..ad6abe9 100644 --- a/src/btop_tools.h +++ b/src/btop_tools.h @@ -17,7 +17,7 @@ tab-size = 4 */ #ifndef _btop_tools_included_ -#define _btop_tools_included_ 1 +#define _btop_tools_included_ #include #include @@ -226,12 +226,27 @@ namespace Term { namespace Tools { //* Return number of UTF8 characters in a string with option to disregard escape sequences - size_t ulen(string s, bool escape=false){ - if (escape) s = std::regex_replace(s, Fx::escape_regex, ""); - return std::count_if(s.begin(), s.end(), + size_t ulen(string str, bool escape=false){ + if (escape) str = std::regex_replace(str, Fx::escape_regex, ""); + return std::count_if(str.begin(), str.end(), [](char c) { return (static_cast(c) & 0xC0) != 0x80; } ); } + //* Resize a string consisting of UTF8 characters (only reduces size) + string uresize(string str, const size_t len){ + if (str.size() < 1) return str; + if (len < 1) return ""; + for (size_t x = 0, i = 0; i < str.size(); i++) { + if ((static_cast(str.at(i)) & 0xC0) != 0x80) x++; + if (x == len) { + str.resize(i + 1); + str.shrink_to_fit(); + break; + } + } + return str; + } + //* Return current time since epoch in seconds uint64_t time_s(){ return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); @@ -301,10 +316,7 @@ namespace Tools { //* Left justify string if is greater than length, limit return size to by default string ljust(string str, const size_t x, bool utf=false, bool escape=false, bool lim=true){ if (utf || escape) { - if (!escape && lim && ulen(str) > x) { - auto i = str.size(); - while (ulen(str) > x) str.resize(--i); - } + if (!escape && lim && ulen(str) > x) str = uresize(str, x); return str + string(max((int)(x - ulen(str, escape)), 0), ' '); } else { @@ -316,10 +328,7 @@ namespace Tools { //* Right justify string if is greater than length, limit return size to by default string rjust(string str, const size_t x, bool utf=false, bool escape=false, bool lim=true){ if (utf || escape) { - if (!escape && lim && ulen(str) > x) { - auto i = str.size(); - while (ulen(str) > x) str.resize(--i); - } + if (!escape && lim && ulen(str) > x) str = uresize(str, x); return string(max((int)(x - ulen(str, escape)), 0), ' ') + str; } else { @@ -328,13 +337,6 @@ namespace Tools { } } - //* Trim trailing characters if utf8 string length is greatear than - string uresize(string str, const size_t x){ - if (str.empty()) return str; - while (ulen(str) > x) str.pop_back(); - return str; - } - //* Replace whitespaces " " with escape code for move right string trans(string str){ size_t pos; @@ -425,10 +427,30 @@ namespace Tools { return ss.str(); } + + #if __GNUC__ > 10 + //* Redirects to atomic wait + void atomic_wait(atomic& atom, bool val=true){ + atom.wait(val); + } + #else + //* Crude implementation of atomic wait for GCC < 11 + void atomic_wait(atomic& atom, bool val=true){ + while (atom.load() == val) sleep_ms(1); + } + #endif + + //* Waits for to not be and then sets it to again + void atomic_wait_set(atomic& atom, bool val){ + atomic_wait(atom, val); + atom.store(val); + } + } //* Simple logging implementation namespace Logger { + using namespace Tools; namespace { std::atomic busy (false); bool first = true; @@ -447,7 +469,7 @@ namespace Logger { void log_write(uint level, string& msg){ if (loglevel < level || logfile.empty()) return; - busy.wait(true); busy.store(true); + atomic_wait_set(busy, true); std::error_code ec; if (fs::file_size(logfile, ec) > 1024 << 10 && !ec) { auto old_log = logfile; @@ -457,8 +479,8 @@ namespace Logger { } if (!ec) { std::ofstream lwrite(logfile, std::ios::app); - if (first) { first = false; lwrite << "\n" << Tools::strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";} - lwrite << Tools::strf_time(tdf) << log_levels[level] << ": " << msg << "\n"; + if (first) { first = false; lwrite << "\n" << strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";} + lwrite << strf_time(tdf) << log_levels[level] << ": " << msg << "\n"; lwrite.close(); } else logfile.clear();