Config thread safety

This commit is contained in:
aristocratos 2021-06-06 01:41:36 +02:00
parent 43e3c4fa87
commit deec8f20e0
8 changed files with 116 additions and 44 deletions

View file

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

View file

@ -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<long long> mydata;
for (long long i = 0; i <= 100; i++) mydata.push_back(i);

View file

@ -17,7 +17,7 @@ tab-size = 4
*/
#ifndef _btop_config_included_
#define _btop_config_included_ 1
#define _btop_config_included_
#include <string>
#include <vector>
@ -38,6 +38,8 @@ namespace Config {
fs::path conf_dir;
fs::path conf_file;
atomic<bool> locked (false);
atomic<bool> writelock (false);
bool changed = false;
unordered_flat_map<string, string> strings = {
@ -57,6 +59,8 @@ namespace Config {
{"net_iface", ""},
{"log_level", "WARNING"}
};
unordered_flat_map<string, string> stringsTmp;
unordered_flat_map<string, bool> bools = {
{"theme_background", true},
{"truecolor", true},
@ -89,11 +93,20 @@ namespace Config {
{"show_battery", true},
{"show_init", false}
};
unordered_flat_map<string, bool> boolsTmp;
unordered_flat_map<string, int> ints = {
{"update_ms", 2000},
{"proc_update_mult", 2},
{"tree_depth", 3}
};
unordered_flat_map<string, int> intsTmp;
bool _locked(){
atomic_wait(writelock);
if (!changed) changed = true;
return locked.load();
}
}
//* Return config value <name> as a bool
@ -113,26 +126,59 @@ namespace Config {
//* Set config value <name> to bool <value>
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 <name> to int <value>
void set(string name, int value){
if (_locked()) intsTmp[name] = value;
ints.at(name) = value;
changed = true;
}
//* Set config value <name> to string <value>
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 <name>
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(){

View file

@ -31,7 +31,7 @@ tab-size = 4
#include <btop_tools.h>
#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;

View file

@ -17,7 +17,7 @@ tab-size = 4
*/
#ifndef _btop_input_included_
#define _btop_input_included_ 1
#define _btop_input_included_
#include <string>
#include <robin_hood.h>

View file

@ -17,7 +17,7 @@ tab-size = 4
*/
#ifndef _btop_linux_included_
#define _btop_linux_included_ 1
#define _btop_linux_included_
#include <string>
#include <vector>

View file

@ -17,7 +17,7 @@ tab-size = 4
*/
#ifndef _btop_theme_included_
#define _btop_theme_included_ 1
#define _btop_theme_included_
#include <string>
#include <cmath>
@ -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;

View file

@ -17,7 +17,7 @@ tab-size = 4
*/
#ifndef _btop_tools_included_
#define _btop_tools_included_ 1
#define _btop_tools_included_
#include <string>
#include <cmath>
@ -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<unsigned char>(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<unsigned char>(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::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
@ -301,10 +316,7 @@ namespace Tools {
//* Left justify string <str> if <x> is greater than <str> length, limit return size to <x> 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 <str> if <x> is greater than <str> length, limit return size to <x> 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 <x>
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<bool>& atom, bool val=true){
atom.wait(val);
}
#else
//* Crude implementation of atomic wait for GCC < 11
void atomic_wait(atomic<bool>& atom, bool val=true){
while (atom.load() == val) sleep_ms(1);
}
#endif
//* Waits for <atom> to not be <val> and then sets it to <val> again
void atomic_wait_set(atomic<bool>& atom, bool val){
atomic_wait(atom, val);
atom.store(val);
}
}
//* Simple logging implementation
namespace Logger {
using namespace Tools;
namespace {
std::atomic<bool> 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();