Added Input::process for input actions and Runner:: namespace for multithreading collection and drawing

This commit is contained in:
aristocratos 2021-07-18 15:44:32 +02:00
parent 1121978214
commit 0c1feb909e
12 changed files with 366 additions and 223 deletions

View file

@ -4,17 +4,11 @@ DOCDIR ?= $(PREFIX)/share/btop/doc
#Compiler and Linker
CXX := g++
#If using g++ try to make sure we are using version 11 or 10
ifeq ($(CXX),g++)
CXX_VERSION = $(shell $(CXX) -dumpversion)
ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0)
ifneq ($(shell command -v g++-11),)
CXX := g++-11
else ifneq ($(shell test $(CXX_VERSION) -eq 10; echo $$?),0)
ifneq ($(shell command -v g++-10),)
CXX := g++-10
endif
endif
#Try to make sure we are using GCC/G++ version 11 or later
CXX_VERSION = $(shell $(CXX) -dumpversion)
ifneq ($(shell test $(CXX_VERSION) -ge 11; echo $$?),0)
ifneq ($(shell command -v g++-11),)
CXX := g++-11
endif
endif
@ -75,7 +69,7 @@ uninstall:
#Link
btop: $(OBJECTS)
$(CXX) -o $(TARGETDIR)/btop $^
$(CXX) -o $(TARGETDIR)/btop $^ -pthread
#Compile
$(BUILDDIR)/%.$(OBJEXT): $(SRCDIR)/%.$(SRCEXT)

View file

@ -126,7 +126,7 @@ Options menu.
#### Manual compilation and installation
Needs at least GCC/G++ 10, preferably 11 (or higher) to make use of some C++20 functionality.
Needs GCC/G++ 11 or higher, (GCC 10 is missing some C++20 features).
>Install dependencies (Ubuntu 21.04 Hirsute)

View file

@ -81,6 +81,9 @@ namespace Global {
string banner;
size_t banner_width = 0;
string exit_error_msg;
atomic<bool> thread_exception (false);
fs::path self_path;
bool debuginit = false;
@ -142,10 +145,7 @@ void argumentParser(int argc, char **argv){
void _resize(bool force=false){
if (Term::refresh(false) or force) {
Global::resized = true;
if (Runner::active) {
Runner::stop = true;
atomic_wait(Runner::active);
}
Runner::stop();
Term::refresh();
}
else return;
@ -160,29 +160,23 @@ void _resize(bool force=false){
}
//* Exit handler; stops threads, restores terminal and saves config changes
void clean_quit(int sig=-1){
void clean_quit(int sig){
if (Global::quitting) return;
Global::quitting = true;
if (Runner::active) {
Runner::stop = true;
atomic_wait(Runner::active);
}
Runner::stop();
if (Term::initialized) {
Term::restore();
if (not Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
}
Config::write();
Logger::info("Quitting! Runtime: " + sec_to_dhms(time_s() - Global::start_time));
if (not Global::exit_error_msg.empty()) cout << Global::exit_error_msg << endl;
if (sig != -1) exit(sig);
}
//* Handler for SIGTSTP; stops threads, restores terminal and sends SIGSTOP
void _sleep(){
if (Runner::active) {
Runner::stop = true;
atomic_wait(Runner::active);
}
Runner::stop();
if (Term::initialized) {
Term::restore();
if (not Global::debuginit) cout << Term::normal_screen << Term::show_cursor << flush;
@ -259,9 +253,97 @@ void banner_gen() {
+ Fx::i + "v" + Global::Version + Fx::ui;
}
//? Manages secondary thread for collection and drawing of boxes
namespace Runner {
atomic<bool> active (false);
atomic<bool> stop (false);
atomic<bool> stopping (false);
atomic<bool> has_output (false);
atomic<uint64_t> time_spent (0);
string output;
void _runner(const vector<string> boxes, const bool no_update, const bool force_redraw, const bool interrupt) {
auto timestamp = time_micros();
string out;
out.reserve(output.size());
try {
for (auto& box : boxes) {
if (stopping) break;
try {
if (box == "cpu") {
out += Cpu::draw(Cpu::collect(no_update), force_redraw);
}
else if (box == "mem") {
out += Mem::draw(Mem::collect(no_update), force_redraw);
}
else if (box == "net") {
out += Net::draw(Net::collect(no_update), force_redraw);
}
else if (box == "proc") {
out += Proc::draw(Proc::collect(no_update), force_redraw);
}
}
catch (const std::exception& e) {
string fname = box;
fname[0] = toupper(fname[0]);
throw std::runtime_error(fname + "::draw(" + fname + "::collect(no_update=" + (no_update ? "true" : "false")
+ "), force_redraw=" + (force_redraw ? "true" : "false") + ") : " + (string)e.what());
}
}
}
catch (std::exception& e) {
Global::exit_error_msg = "Exception in runner thread -> " + (string)e.what();
Logger::error(Global::exit_error_msg);
Global::thread_exception = true;
Input::interrupt = true;
stopping = true;
}
if (stopping) {
active = false;
active.notify_all();
return;
}
if (out.empty()) {
out += "No boxes shown!";
}
output.swap(out);
time_spent = time_micros() - timestamp;
has_output = true;
if (interrupt) Input::interrupt = true;
active = false;
active.notify_one();
}
void run(const string box, const bool no_update, const bool force_redraw, const bool input_interrupt) {
active.wait(true);
if (stopping) return;
active = true;
Config::lock();
vector<string> boxes;
if (box.empty()) boxes = Config::current_boxes;
else boxes.push_back(box);
std::thread run_thread(_runner, boxes, no_update, force_redraw, input_interrupt);
run_thread.detach();
}
void stop() {
stopping = true;
active.wait(true);
Config::unlock();
stopping = false;
}
string get_output() {
active.wait(true);
Config::unlock();
has_output = false;
return output;
}
}
@ -396,27 +478,6 @@ int main(int argc, char **argv){
cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
if (false) {
Draw::calcSizes();
cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
Input::wait();
exit(0);
}
// if (true) {
// cout << Term::clear << flush;
// unordered_flat_map<string, string(*)(string)> korvs = {
// {"korv1", korv1},
// {"korv2", korv2},
// };
// // auto hej = korv1;
// cout << korvs["korv1"]("hejsan") << endl;
// cout << korvs["korv2"]("hejsan igen") << endl;
// exit(0);
// }
//* Test theme
if (false) {
string key;
@ -474,7 +535,7 @@ int main(int argc, char **argv){
exit(0);
}
//* Test graphs
if (false) {
deque<long long> mydata;
@ -524,119 +585,49 @@ int main(int argc, char **argv){
}
if (false) {
cout << Config::getS("log_level") << endl;
vector<string> vv = {"hej", "vad", "du"};
vector<int> vy;
//* ------------------------------------------------ MAIN LOOP ----------------------------------------------------
cout << v_contains(vv, "vad"s) << endl;
cout << v_index(vv, "hej"s) << endl;
cout << v_index(vv, "du"s) << endl;
cout << v_index(vv, "kodkod"s) << endl;
cout << v_index(vy, 4) << endl;
exit(0);
}
//*------>>>>>> Proc testing
auto timestamp = time_ms();
string ostring;
uint64_t tsl, timestamp2, rcount = 0;
uint64_t future_time = time_ms(), rcount = 0;
list<uint64_t> avgtimes;
size_t timer = 2000;
vector<string> greyscale;
string filter;
string filter_cur;
string key;
vector<Proc::proc_info> plist;
int xc;
for (size_t i : iota(0, (int)Term::height - 19)){
xc = 230 - i * 150 / (Term::height - 20);
greyscale.push_back(Theme::dec_to_color(xc, xc, xc));
try {
while (true) {
if (Global::thread_exception) clean_quit(1);
if (Runner::has_output) {
cout << Term::sync_start << Runner::get_output() << Term::sync_end << flush;
//! DEBUG stats
avgtimes.push_front(Runner::time_spent);
if (avgtimes.size() > 30) avgtimes.pop_back();
cout << Fx::reset << Mv::to(2, 2) << "Runner took: " << rjust(to_string(avgtimes.front()), 5) << " μs. Average: " <<
rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 5) << " μs of " << avgtimes.size() <<
" samples. Run count: " << ++rcount << ". " << flush;
}
if (time_ms() >= future_time) {
Runner::run();
future_time = time_ms() + Config::getI("update_ms");
}
while (time_ms() < future_time and not Global::resized) {
if (Input::poll(future_time - time_ms()))
Input::process(Input::get());
else
break;
}
if (Global::resized) {
// cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
Global::resized = false;
future_time = time_ms();
}
}
}
while (key != "q") {
timestamp = time_micros();
tsl = time_ms() + timer;
Config::lock();
try {
plist = Proc::collect();
}
catch (std::exception const& e) {
Logger::error("Caught exception in Proc::collect() : "s + e.what());
exit(1);
}
timestamp2 = time_micros();
timestamp = timestamp2 - timestamp;
ostring.clear();
ostring = Proc::draw(plist);
Config::unlock();
avgtimes.push_front(timestamp);
if (avgtimes.size() > 30) avgtimes.pop_back();
cout << Term::sync_start << ostring << Fx::reset << Mv::to(2, 2) << '\n';
cout << " Details for " << Proc::detailed.entry.name << " (" << Proc::detailed.entry.pid << ") Status: " << Proc::detailed.status << " Elapsed: " << Proc::detailed.elapsed
<< " Mem: " << floating_humanizer(Proc::detailed.entry.mem) << " "
<< "\n Parent: " << Proc::detailed.parent << " IO in/out: " << Proc::detailed.io_read << "/" << Proc::detailed.io_write << " ";
cout << Mv::to(4, 2) << "Processes call took: " << rjust(to_string(timestamp), 5) << " μs. Average: " <<
rjust(to_string(accumulate(avgtimes.begin(), avgtimes.end(), 0) / avgtimes.size()), 5) << " μs of " << avgtimes.size() <<
" samples. Drawing took: " << time_micros() - timestamp2 << " μs.\nNumber of processes: " << Proc::numpids << ". Number in vector: " << plist.size() << ". Run count: " << ++rcount << ". Time: " << strf_time("%X ") << Term::sync_end << flush;
while (time_ms() < tsl and not Global::resized) {
if (Input::poll(tsl - time_ms())) key = Input::get();
else { key.clear() ; continue; }
if (Config::getB("proc_filtering")) {
if (key == "enter") Config::set("proc_filtering", false);
else if (key == "backspace" and not filter.empty()) filter = uresize(filter, ulen(filter) - 1);
else if (key == "space") filter.push_back(' ');
else if (ulen(key) == 1 ) filter.append(key);
else { key.clear(); continue; }
if (filter != Config::getS("proc_filter")) Config::set("proc_filter", filter);
key.clear();
Proc::redraw = true;
break;
}
else if (key == "q") break;
else if (key == "left") {
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
if (--cur_i < 0) cur_i = Proc::sort_vector.size() - 1;
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
}
else if (key == "right") {
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
if (++cur_i > (int)Proc::sort_vector.size() - 1) cur_i = 0;
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
}
else if (key == "f") Config::flip("proc_filtering");
else if (key == "t") Config::flip("proc_tree");
else if (key == "r") Config::flip("proc_reversed");
else if (key == "c") Config::flip("proc_per_core");
else if (key == "delete") { filter.clear(); Config::set("proc_filter", filter); }
else if (is_in(key, "up", "down", "page_up", "page_down", "home", "end")) {
Proc::selection(key);
cout << Proc::draw(plist) << flush;
continue;
}
else continue;
Proc::redraw = true;
break;
}
if (Global::resized) {
cout << Cpu::box << Mem::box << Net::box << Proc::box << flush;
Global::resized = false;
}
cout << Mv::to(Term::height - 3, 1) << flush;
catch (std::exception& e) {
Global::exit_error_msg = "Exception in main loop -> " + (string)e.what();
Logger::error(Global::exit_error_msg);
clean_quit(1);
}

View file

@ -230,7 +230,7 @@ namespace Config {
vector<string> valid_boxes = { "cpu", "mem", "net", "proc" };
bool _locked(const string& name){
atomic_wait(writelock);
writelock.wait(true);
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();
@ -240,17 +240,19 @@ namespace Config {
fs::path conf_dir;
fs::path conf_file;
vector<string> current_boxes;
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
const bool& getB(string name){
const bool& getB(const string& name){
return bools.at(name);
}
const int& getI(string name){
const int& getI(const string& name){
return ints.at(name);
}
const string& getS(string name){
const string& getS(const string& name){
return strings.at(name);
}
@ -278,13 +280,19 @@ namespace Config {
}
void lock(){
atomic_wait_set(locked);
writelock.wait(true);
locked = true;
}
void unlock(){
if (not locked) return;
atomic_wait(Runner::active);
atomic_wait_set(writelock);
writelock = true;
if (Proc::shown) {
ints.at("selected_pid") = Proc::selected_pid;
ints.at("proc_start") = Proc::start;
ints.at("proc_selected") = Proc::selected;
}
for (auto& item : stringsTmp){
strings.at(item.first) = item.second;
@ -301,20 +309,17 @@ namespace Config {
}
boolsTmp.clear();
if (Proc::shown) {
ints.at("selected_pid") = Proc::selected_pid;
ints.at("proc_start") = Proc::start;
ints.at("proc_selected") = Proc::selected;
}
locked = false;
writelock = false;
writelock.notify_all();
}
bool check_boxes(string boxes){
for (auto& box : ssplit(boxes)) {
auto new_boxes = ssplit(boxes);
for (auto& box : new_boxes) {
if (not v_contains(valid_boxes, box)) return false;
}
current_boxes.swap(new_boxes);
return true;
}
@ -342,10 +347,12 @@ namespace Config {
}
string name, value;
getline(cread, name, '=');
if (name.ends_with(' ')) name = trim(name);
if (not v_contains(valid_names, name)) {
cread.ignore(SSmax, '\n');
continue;
}
cread >> std::ws;
if (bools.contains(name)) {
cread >> value;
@ -362,7 +369,6 @@ namespace Config {
ints.at(name) = stoi(value);
}
else if (strings.contains(name)) {
cread >> std::ws;
if (cread.peek() == '"') {
cread.ignore(1);
getline(cread, value, '"');
@ -393,10 +399,14 @@ namespace Config {
if (cwrite.good()) {
cwrite << "#? Config file for btop v. " << Global::Version;
for (auto [name, description] : descriptions) {
cwrite << "\n\n" << (description.empty() ? "" : description + "\n") << name << "=";
if (strings.contains(name)) cwrite << "\"" << strings.at(name) << "\"";
else if (ints.contains(name)) cwrite << ints.at(name);
else if (bools.contains(name)) cwrite << (bools.at(name) ? "True" : "False");
cwrite << "\n\n" << (description.empty() ? "" : description + "\n")
<< name << " = ";
if (strings.contains(name))
cwrite << "\"" << strings.at(name) << "\"";
else if (ints.contains(name))
cwrite << ints.at(name);
else if (bools.contains(name))
cwrite << (bools.at(name) ? "True" : "False");
}
cwrite.close();
}

View file

@ -32,17 +32,19 @@ namespace Config {
extern const vector<string> valid_graph_symbols;
extern vector<string> current_boxes;
//* Check if string only contains space seperated valid names for boxes
bool check_boxes(string boxes);
//* Return bool for config key <name>
const bool& getB(string name);
const bool& getB(const string& name);
//* Return integer for config key <name>
const int& getI(string name);
const int& getI(const string& name);
//* Return string for config key <name>
const string& getS(string name);
const string& getS(const string& name);
//* Set config key <name> to bool <value>
void set(string name, bool value);

View file

@ -275,6 +275,16 @@ namespace Cpu {
bool shown = true, redraw = true;
string box;
string draw(const cpu_info& cpu, bool force_redraw) {
(void)cpu;
string out;
if (redraw or force_redraw) {
redraw = false;
out += box;
}
return out;
}
}
namespace Mem {
@ -285,6 +295,16 @@ namespace Mem {
bool shown = true, redraw = true;
string box;
string draw(const mem_info& mem, bool force_redraw) {
(void)mem;
string out;
if (redraw or force_redraw) {
redraw = false;
out += box;
}
return out;
}
}
namespace Net {
@ -296,6 +316,16 @@ namespace Net {
bool shown = true, redraw = true;
string box;
string draw(const net_info& net, bool force_redraw) {
(void)net;
string out;
if (redraw or force_redraw) {
redraw = false;
out += box;
}
return out;
}
}
namespace Proc {
@ -311,6 +341,7 @@ namespace Proc {
void selection(string cmd_key) {
auto start = Config::getI("proc_start");
auto selected = Config::getI("proc_selected");
int numpids = Proc::numpids;
if (cmd_key == "up" and selected > 0) {
if (start > 0 and selected == 1) start--;
else selected--;
@ -340,7 +371,7 @@ namespace Proc {
Config::set("proc_selected", selected);
}
string draw(vector<proc_info> plist){
string draw(const vector<proc_info>& plist, bool force_redraw){
auto& filter = Config::getS("proc_filter");
auto& filtering = Config::getB("proc_filtering");
auto& proc_tree = Config::getB("proc_tree");
@ -352,9 +383,10 @@ namespace Proc {
uint64_t total_mem = 16328872 << 10;
int y = show_detailed ? Proc::y + 9 : Proc::y;
int height = show_detailed ? Proc::height - 9 : Proc::height;
int numpids = Proc::numpids;
string out;
//* If true, redraw elements not needed to be updated every cycle
if (redraw) {
if (redraw or force_redraw) {
redraw = false;
out = box;
out += Mv::to(y, x) + Mv::r(12)

View file

@ -22,8 +22,10 @@ tab-size = 4
#include <btop_input.hpp>
#include <btop_tools.hpp>
#include <btop_config.hpp>
#include <btop_shared.hpp>
using std::string, robin_hood::unordered_flat_map, std::cin;
using std::string, robin_hood::unordered_flat_map, std::cin, std::string_literals::operator""s;
using namespace Tools;
namespace Input {
@ -95,7 +97,7 @@ namespace Input {
string wait(){
while (cin.rdbuf()->in_avail() < 1) {
if (interrupt) { interrupt = false; return string(); }
if (interrupt) { interrupt = false; return ""; }
sleep_ms(10);
}
return get();
@ -105,4 +107,59 @@ namespace Input {
last.clear();
}
void process(const string key){
if (key.empty()) return;
try {
auto& filtering = Config::getB("proc_filtering");
if (not filtering and key == "q") clean_quit(0);
bool recollect = true;
bool redraw = true;
//* Input actions for proc
if (Proc::shown) {
bool keep_going = false;
if (filtering) {
string filter = Config::getS("proc_filter");
if (key == "enter") Config::set("proc_filtering", false);
else if (key == "backspace" and not filter.empty()) filter = uresize(filter, ulen(filter) - 1);
else if (key == "space") filter.push_back(' ');
else if (ulen(key) == 1) filter.append(key);
else return;
Config::set("proc_filter", filter);
}
else if (key == "left") {
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
if (--cur_i < 0) cur_i = Proc::sort_vector.size() - 1;
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
}
else if (key == "right") {
int cur_i = v_index(Proc::sort_vector, Config::getS("proc_sorting"));
if (++cur_i > (int)Proc::sort_vector.size() - 1) cur_i = 0;
Config::set("proc_sorting", Proc::sort_vector.at(cur_i));
}
else if (key == "f") { Config::flip("proc_filtering"); recollect = false; }
else if (key == "t") Config::flip("proc_tree");
else if (key == "r") Config::flip("proc_reversed");
else if (key == "c") Config::flip("proc_per_core");
else if (key == "delete" and not Config::getS("proc_filter").empty()) Config::set("proc_filter", ""s);
else if (is_in(key, "up", "down", "page_up", "page_down", "home", "end")) {
Proc::selection(key);
recollect = false;
redraw = false;
}
else keep_going = true;
if (not keep_going) {
Runner::run("proc", not recollect, redraw);
return;
}
}
}
catch (const std::exception& e) {
throw std::runtime_error("Input::process(\"" + key + "\") : " + (string)e.what());
}
}
}

View file

@ -47,4 +47,7 @@ namespace Input {
//* Clears last entered key
void clear();
//* Process actions for input <key>
void process(const std::string key);
}

View file

@ -93,10 +93,34 @@ namespace Shared {
namespace Cpu {
bool got_sensors = false;
string cpuName = "";
cpu_info current_cpu;
cpu_info& collect(const bool return_last) {
(void)return_last;
return current_cpu;
}
}
namespace Mem {
bool has_swap = false;
mem_info current_mem;
mem_info& collect(const bool return_last) {
(void)return_last;
return current_mem;
}
}
namespace Net {
net_info current_net;
net_info& collect(const bool return_last) {
(void)return_last;
return current_net;
}
}
namespace Proc {
@ -117,11 +141,9 @@ namespace Proc {
int counter = 0;
}
uint64_t old_cputimes = 0;
int numpids = 0;
atomic<int> numpids = 0;
size_t reserve_pids = 500;
bool tree_state = false;
atomic<bool> stop (false);
atomic<bool> collecting (false);
vector<string> sort_vector = {
"pid",
"name",
@ -150,6 +172,7 @@ namespace Proc {
//* Generate process tree list
void _tree_gen(const proc_info& cur_proc, const vector<proc_info>& in_procs, vector<proc_info>& out_procs, int cur_depth, const bool collapsed, const string& filter, bool found=false){
if (Runner::stopping) return;
auto cur_pos = out_procs.size();
bool filtering = false;
@ -290,9 +313,8 @@ namespace Proc {
}
//* Collects and sorts process information from /proc
vector<proc_info>& collect(bool return_last){
vector<proc_info>& collect(const bool return_last){
if (return_last) return current_procs;
atomic_wait_set(collecting);
auto& sorting = Config::getS("proc_sorting");
auto reverse = Config::getB("proc_reversed");
auto& filter = Config::getS("proc_filter");
@ -345,12 +367,9 @@ namespace Proc {
//* Iterate over all pids in /proc
for (auto& d: fs::directory_iterator(Shared::proc_path)){
if (pread.is_open()) pread.close();
if (stop) {
collecting = false;
stop = false;
if (Runner::stopping)
return current_procs;
}
if (pread.is_open()) pread.close();
string pid_str = d.path().filename();
if (not isdigit(pid_str[0])) continue;
@ -554,6 +573,8 @@ namespace Proc {
for (auto& p : rng::equal_range(procs, procs.at(0).ppid, rng::less{}, &proc_info::ppid)) {
_tree_gen(p, procs, tree_procs, 0, cache.at(p.pid).collapsed, filter);
}
if (Runner::stopping) return current_procs;
procs.swap(tree_procs);
}
@ -574,7 +595,6 @@ namespace Proc {
numpids = (int)procs.size();
current_procs.swap(procs);
reserve_pids = npids;
collecting = false;
return current_procs;
}
}

View file

@ -24,11 +24,16 @@ tab-size = 4
#include <atomic>
#include <deque>
#include <robin_hood.h>
#include <array>
using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic;
using std::string, std::vector, std::deque, robin_hood::unordered_flat_map, std::atomic, std::array;
void clean_quit(int sig=-1);
namespace Global {
extern const string Version;
extern string exit_error_msg;
extern atomic<bool> thread_exception;
extern int coreCount;
extern string banner;
}
@ -36,7 +41,10 @@ namespace Global {
namespace Runner {
extern atomic<bool> active;
extern atomic<bool> stop;
extern atomic<bool> stopping;
void run(const string box="", const bool no_update=false, const bool force_redraw=false, const bool input_interrupt=true);
void stop();
}
@ -53,22 +61,65 @@ namespace Shared {
namespace Cpu {
extern string box, cpuName;
extern bool shown, redraw, got_sensors;
struct cpu_info {
vector<deque<long long>> percent;
vector<deque<long long>> temp;
array<float, 3> load_avg;
};
//* Collect cpu stats and temperatures
cpu_info& collect(const bool return_last=false);
//* Draw contents of cpu box using <cpu> as source
string draw(const cpu_info& cpu, bool force_redraw=false);
}
namespace Mem {
extern string box;
extern bool has_swap, shown, redraw;
struct disk_info {
uint64_t total = 0, used = 0;
};
struct mem_info {
uint64_t total = 0, available = 0, cached = 0, free = 0;
unordered_flat_map<string, deque<long long>> percent;
unordered_flat_map<string, deque<long long>> disks_io;
unordered_flat_map<string, disk_info> disks;
};
//* Collect mem & disks stats
mem_info& collect(const bool return_last=false);
//* Draw contents of mem box using <mem> as source
string draw(const mem_info& mem, bool force_redraw=false);
}
namespace Net {
extern string box;
extern bool shown, redraw;
struct net_stat {
uint64_t speed = 0, top = 0, total = 0;
};
struct net_info {
unordered_flat_map<string, deque<long long>> bandwidth;
unordered_flat_map<string, net_stat> stat;
string ip_addr;
};
//* Collect net upload/download stats
net_info& collect(const bool return_last=false);
//* Draw contents of net box using <net> as source
string draw(const net_info& net, bool force_redraw=false);
}
namespace Proc {
extern int numpids;
extern atomic<bool> stop;
extern atomic<bool> collecting;
extern atomic<int> numpids;
extern string box;
extern bool shown, redraw;
@ -106,12 +157,12 @@ namespace Proc {
extern detail_container detailed;
//* Collect and sort process information from /proc, saves and returns reference to Proc::current_procs;
vector<proc_info>& collect(bool return_last=false);
//* Collect and sort process information from /proc
vector<proc_info>& collect(const bool return_last=false);
//* Update current selection and view
void selection(string cmd_key);
//* Draw contents of proc box using <plist> as data source
string draw(vector<proc_info> plist);
string draw(const vector<proc_info>& plist, bool force_redraw=false);
}

View file

@ -329,7 +329,8 @@ namespace Logger {
void log_write(const size_t level, const string& msg){
if (loglevel < level or logfile.empty()) return;
atomic_wait_set(busy, true);
busy.wait(true);
busy = true;
std::error_code ec;
if (fs::exists(logfile) and fs::file_size(logfile, ec) > 1024 << 10 and not ec) {
auto old_log = logfile;
@ -345,5 +346,6 @@ namespace Logger {
}
else logfile.clear();
busy = false;
busy.notify_one();
}
}

View file

@ -267,25 +267,6 @@ namespace Tools {
//* Return current time in <strf> format
string strf_time(const string& strf);
//* Waits for <atom> to not be <val>
#if (__GNUC__ > 10)
//* Redirects to atomic wait
inline void atomic_wait(const atomic<bool>& atom, bool val=true){
atom.wait(val);
}
#else
//* Crude implementation of atomic wait for GCC 10
inline void atomic_wait(const atomic<bool>& atom, bool val=true){
while (atom == val) std::this_thread::sleep_for(std::chrono::microseconds(1));
}
#endif
//* Waits for <atom> to not be <val> and then sets it to <val> again
inline void atomic_wait_set(std::atomic<bool>& atom, bool val=true){
atomic_wait(atom, val);
atom = val;
};
}
//* Simple logging implementation