mirror of
https://github.com/aristocratos/btop.git
synced 2024-06-02 10:35:14 +12:00
Config thread safety
This commit is contained in:
parent
43e3c4fa87
commit
deec8f20e0
|
@ -112,7 +112,9 @@ Look to the creators of the terminal emulator you use to fix these issues if the
|
||||||
|
|
||||||
None
|
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
|
## Screenshots
|
||||||
|
|
||||||
|
@ -132,7 +134,7 @@ Options menu.
|
||||||
|
|
||||||
#### Manual compilation and installation
|
#### Manual compilation and installation
|
||||||
|
|
||||||
Requires GCC/G++ 11!
|
Requires GCC/G++ 10 or higher!
|
||||||
|
|
||||||
>Clone and compile
|
>Clone and compile
|
||||||
|
|
||||||
|
|
14
btop.cpp
14
btop.cpp
|
@ -179,6 +179,7 @@ int main(int argc, char **argv){
|
||||||
|
|
||||||
std::atexit(_exit_handler);
|
std::atexit(_exit_handler);
|
||||||
|
|
||||||
|
//? Linux init
|
||||||
#if defined(LINUX)
|
#if defined(LINUX)
|
||||||
Global::coreCount = sysconf(_SC_NPROCESSORS_ONLN);
|
Global::coreCount = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
if (Global::coreCount < 1) Global::coreCount = 1;
|
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 (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")) {
|
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);
|
Logger::warning(err_msg);
|
||||||
cout << "WARNING: " << err_msg << endl;
|
cout << "WARNING: " << err_msg << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
//? Initialize terminal and set options
|
//? Initialize terminal and set options
|
||||||
if (!Term::init()) {
|
if (!Term::init()) {
|
||||||
string err_msg = "No tty detected!";
|
string err_msg = "No tty detected!\nbtop++ needs an interactive shell to run.";
|
||||||
Logger::error(err_msg + " Quitting.");
|
Logger::error(err_msg);
|
||||||
cout << "ERROR: " << err_msg << endl;
|
cout << "ERROR: " << err_msg << endl;
|
||||||
cout << "btop++ needs an interactive shell to run." << endl;
|
|
||||||
clean_quit(1);
|
clean_quit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,7 +247,7 @@ int main(int argc, char **argv){
|
||||||
|
|
||||||
//? Read config file if present
|
//? Read config file if present
|
||||||
Config::load();
|
Config::load();
|
||||||
// Config::setB("truecolor", false);
|
// Config::set("truecolor", false);
|
||||||
|
|
||||||
auto thts = time_ms();
|
auto thts = time_ms();
|
||||||
|
|
||||||
|
@ -311,6 +312,7 @@ int main(int argc, char **argv){
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (false) {
|
if (false) {
|
||||||
Draw::Meter kmeter;
|
Draw::Meter kmeter;
|
||||||
kmeter(Term::width - 2, "cpu", false);
|
kmeter(Term::width - 2, "cpu", false);
|
||||||
|
@ -336,7 +338,7 @@ int main(int argc, char **argv){
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true) {
|
if (false) {
|
||||||
|
|
||||||
vector<long long> mydata;
|
vector<long long> mydata;
|
||||||
for (long long i = 0; i <= 100; i++) mydata.push_back(i);
|
for (long long i = 0; i <= 100; i++) mydata.push_back(i);
|
||||||
|
|
|
@ -17,7 +17,7 @@ tab-size = 4
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _btop_config_included_
|
#ifndef _btop_config_included_
|
||||||
#define _btop_config_included_ 1
|
#define _btop_config_included_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
@ -38,6 +38,8 @@ namespace Config {
|
||||||
fs::path conf_dir;
|
fs::path conf_dir;
|
||||||
fs::path conf_file;
|
fs::path conf_file;
|
||||||
|
|
||||||
|
atomic<bool> locked (false);
|
||||||
|
atomic<bool> writelock (false);
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
|
|
||||||
unordered_flat_map<string, string> strings = {
|
unordered_flat_map<string, string> strings = {
|
||||||
|
@ -57,6 +59,8 @@ namespace Config {
|
||||||
{"net_iface", ""},
|
{"net_iface", ""},
|
||||||
{"log_level", "WARNING"}
|
{"log_level", "WARNING"}
|
||||||
};
|
};
|
||||||
|
unordered_flat_map<string, string> stringsTmp;
|
||||||
|
|
||||||
unordered_flat_map<string, bool> bools = {
|
unordered_flat_map<string, bool> bools = {
|
||||||
{"theme_background", true},
|
{"theme_background", true},
|
||||||
{"truecolor", true},
|
{"truecolor", true},
|
||||||
|
@ -89,11 +93,20 @@ namespace Config {
|
||||||
{"show_battery", true},
|
{"show_battery", true},
|
||||||
{"show_init", false}
|
{"show_init", false}
|
||||||
};
|
};
|
||||||
|
unordered_flat_map<string, bool> boolsTmp;
|
||||||
|
|
||||||
unordered_flat_map<string, int> ints = {
|
unordered_flat_map<string, int> ints = {
|
||||||
{"update_ms", 2000},
|
{"update_ms", 2000},
|
||||||
{"proc_update_mult", 2},
|
{"proc_update_mult", 2},
|
||||||
{"tree_depth", 3}
|
{"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
|
//* Return config value <name> as a bool
|
||||||
|
@ -113,26 +126,59 @@ namespace Config {
|
||||||
|
|
||||||
//* Set config value <name> to bool <value>
|
//* Set config value <name> to bool <value>
|
||||||
void set(string name, bool value){
|
void set(string name, bool value){
|
||||||
bools.at(name) = value;
|
if (_locked()) boolsTmp[name] = value;
|
||||||
changed = true;
|
else bools.at(name) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Set config value <name> to int <value>
|
//* Set config value <name> to int <value>
|
||||||
void set(string name, int value){
|
void set(string name, int value){
|
||||||
|
if (_locked()) intsTmp[name] = value;
|
||||||
ints.at(name) = value;
|
ints.at(name) = value;
|
||||||
changed = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Set config value <name> to string <value>
|
//* Set config value <name> to string <value>
|
||||||
void set(string name, string value){
|
void set(string name, string value){
|
||||||
strings.at(name) = value;
|
if (_locked()) stringsTmp[name] = value;
|
||||||
changed = true;
|
else strings.at(name) = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
//* Flip config bool value
|
//* Flip config bool <name>
|
||||||
void flip(string name){
|
void flip(string name){
|
||||||
bools.at(name) = !bools.at(name);
|
if (_locked()) {
|
||||||
changed = true;
|
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(){
|
void load(){
|
||||||
|
|
|
@ -31,7 +31,7 @@ tab-size = 4
|
||||||
#include <btop_tools.h>
|
#include <btop_tools.h>
|
||||||
|
|
||||||
#ifndef _btop_draw_included_
|
#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,
|
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;
|
std::string_literals::operator""s, std::clamp, std::array, std::floor;
|
||||||
|
|
|
@ -17,7 +17,7 @@ tab-size = 4
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _btop_input_included_
|
#ifndef _btop_input_included_
|
||||||
#define _btop_input_included_ 1
|
#define _btop_input_included_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <robin_hood.h>
|
#include <robin_hood.h>
|
||||||
|
|
|
@ -17,7 +17,7 @@ tab-size = 4
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _btop_linux_included_
|
#ifndef _btop_linux_included_
|
||||||
#define _btop_linux_included_ 1
|
#define _btop_linux_included_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
|
@ -17,7 +17,7 @@ tab-size = 4
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _btop_theme_included_
|
#ifndef _btop_theme_included_
|
||||||
#define _btop_theme_included_ 1
|
#define _btop_theme_included_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -240,7 +240,7 @@ namespace Theme {
|
||||||
int rng = (rgb_arr[1][0] >= 0) ? 50 : 100;
|
int rng = (rgb_arr[1][0] >= 0) ? 50 : 100;
|
||||||
for (int rgb : iota(0, 3)){
|
for (int rgb : iota(0, 3)){
|
||||||
int arr1 = 0, offset = 0;
|
int arr1 = 0, offset = 0;
|
||||||
int arr2 = (rng == 50) ? 1 : 2;
|
int arr2 = (rng == 50) ? 1 : 2;
|
||||||
for (int i : iota(0, 101)) {
|
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;
|
dec_arr[i][rgb] = rgb_arr[arr1][rgb] + (i - offset) * (rgb_arr[arr2][rgb] - rgb_arr[arr1][rgb]) / rng;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ tab-size = 4
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _btop_tools_included_
|
#ifndef _btop_tools_included_
|
||||||
#define _btop_tools_included_ 1
|
#define _btop_tools_included_
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
@ -226,12 +226,27 @@ namespace Term {
|
||||||
namespace Tools {
|
namespace Tools {
|
||||||
|
|
||||||
//* Return number of UTF8 characters in a string with option to disregard escape sequences
|
//* Return number of UTF8 characters in a string with option to disregard escape sequences
|
||||||
size_t ulen(string s, bool escape=false){
|
size_t ulen(string str, bool escape=false){
|
||||||
if (escape) s = std::regex_replace(s, Fx::escape_regex, "");
|
if (escape) str = std::regex_replace(str, Fx::escape_regex, "");
|
||||||
return std::count_if(s.begin(), s.end(),
|
return std::count_if(str.begin(), str.end(),
|
||||||
[](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; } );
|
[](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
|
//* Return current time since epoch in seconds
|
||||||
uint64_t time_s(){
|
uint64_t time_s(){
|
||||||
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
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
|
//* 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){
|
string ljust(string str, const size_t x, bool utf=false, bool escape=false, bool lim=true){
|
||||||
if (utf || escape) {
|
if (utf || escape) {
|
||||||
if (!escape && lim && ulen(str) > x) {
|
if (!escape && lim && ulen(str) > x) str = uresize(str, x);
|
||||||
auto i = str.size();
|
|
||||||
while (ulen(str) > x) str.resize(--i);
|
|
||||||
}
|
|
||||||
return str + string(max((int)(x - ulen(str, escape)), 0), ' ');
|
return str + string(max((int)(x - ulen(str, escape)), 0), ' ');
|
||||||
}
|
}
|
||||||
else {
|
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
|
//* 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){
|
string rjust(string str, const size_t x, bool utf=false, bool escape=false, bool lim=true){
|
||||||
if (utf || escape) {
|
if (utf || escape) {
|
||||||
if (!escape && lim && ulen(str) > x) {
|
if (!escape && lim && ulen(str) > x) str = uresize(str, x);
|
||||||
auto i = str.size();
|
|
||||||
while (ulen(str) > x) str.resize(--i);
|
|
||||||
}
|
|
||||||
return string(max((int)(x - ulen(str, escape)), 0), ' ') + str;
|
return string(max((int)(x - ulen(str, escape)), 0), ' ') + str;
|
||||||
}
|
}
|
||||||
else {
|
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
|
//* Replace whitespaces " " with escape code for move right
|
||||||
string trans(string str){
|
string trans(string str){
|
||||||
size_t pos;
|
size_t pos;
|
||||||
|
@ -425,10 +427,30 @@ namespace Tools {
|
||||||
return ss.str();
|
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
|
//* Simple logging implementation
|
||||||
namespace Logger {
|
namespace Logger {
|
||||||
|
using namespace Tools;
|
||||||
namespace {
|
namespace {
|
||||||
std::atomic<bool> busy (false);
|
std::atomic<bool> busy (false);
|
||||||
bool first = true;
|
bool first = true;
|
||||||
|
@ -447,7 +469,7 @@ namespace Logger {
|
||||||
|
|
||||||
void log_write(uint level, string& msg){
|
void log_write(uint level, string& msg){
|
||||||
if (loglevel < level || logfile.empty()) return;
|
if (loglevel < level || logfile.empty()) return;
|
||||||
busy.wait(true); busy.store(true);
|
atomic_wait_set(busy, true);
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
if (fs::file_size(logfile, ec) > 1024 << 10 && !ec) {
|
if (fs::file_size(logfile, ec) > 1024 << 10 && !ec) {
|
||||||
auto old_log = logfile;
|
auto old_log = logfile;
|
||||||
|
@ -457,8 +479,8 @@ namespace Logger {
|
||||||
}
|
}
|
||||||
if (!ec) {
|
if (!ec) {
|
||||||
std::ofstream lwrite(logfile, std::ios::app);
|
std::ofstream lwrite(logfile, std::ios::app);
|
||||||
if (first) { first = false; lwrite << "\n" << Tools::strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";}
|
if (first) { first = false; lwrite << "\n" << strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";}
|
||||||
lwrite << Tools::strf_time(tdf) << log_levels[level] << ": " << msg << "\n";
|
lwrite << strf_time(tdf) << log_levels[level] << ": " << msg << "\n";
|
||||||
lwrite.close();
|
lwrite.close();
|
||||||
}
|
}
|
||||||
else logfile.clear();
|
else logfile.clear();
|
||||||
|
|
Loading…
Reference in a new issue