feat: fix deadlocks

This commit is contained in:
Cyrix126 2024-10-10 19:45:41 +02:00
parent 5230d46d93
commit 387f386573
10 changed files with 538 additions and 540 deletions

View file

@ -27,6 +27,12 @@ impl eframe::App for App {
// These values are checked multiple times so // These values are checked multiple times so
// might as well check only once here to save // might as well check only once here to save
// on a bunch of [.lock().unwrap()]s. // on a bunch of [.lock().unwrap()]s.
debug!("App | Locking and collecting Node state...");
let node = self.node.lock().unwrap();
let node_is_alive = node.is_alive();
let node_is_waiting = node.is_waiting();
let node_state = node.state;
drop(node);
debug!("App | Locking and collecting P2Pool state..."); debug!("App | Locking and collecting P2Pool state...");
let p2pool = self.p2pool.lock().unwrap(); let p2pool = self.p2pool.lock().unwrap();
let p2pool_is_alive = p2pool.is_alive(); let p2pool_is_alive = p2pool.is_alive();
@ -49,14 +55,9 @@ impl eframe::App for App {
let xvb = self.xvb.lock().unwrap(); let xvb = self.xvb.lock().unwrap();
let xvb_is_alive = xvb.is_alive(); let xvb_is_alive = xvb.is_alive();
let xvb_is_waiting = xvb.is_waiting(); let xvb_is_waiting = xvb.is_waiting();
let xvb_is_running = xvb.state == ProcessState::Alive;
let xvb_state = xvb.state; let xvb_state = xvb.state;
drop(xvb); drop(xvb);
debug!("App | Locking and collecting Node state...");
let node = self.node.lock().unwrap();
let node_is_alive = node.is_alive();
let node_is_waiting = node.is_waiting();
let node_state = node.state;
drop(node);
// This sets the top level Ui dimensions. // This sets the top level Ui dimensions.
// Used as a reference for other uis. // Used as a reference for other uis.
@ -139,6 +140,7 @@ impl eframe::App for App {
xmrig_is_alive, xmrig_is_alive,
xmrig_proxy_is_alive, xmrig_proxy_is_alive,
xvb_is_alive, xvb_is_alive,
xvb_is_running,
); );
} }
} }

View file

@ -1,6 +1,5 @@
use crate::app::keys::KeyPressed; use crate::app::keys::KeyPressed;
use crate::app::Tab; use crate::app::Tab;
use crate::helper::ProcessState;
use crate::utils::constants::*; use crate::utils::constants::*;
use crate::utils::errors::{ErrorButtons, ErrorFerris}; use crate::utils::errors::{ErrorButtons, ErrorFerris};
use egui::*; use egui::*;
@ -25,6 +24,7 @@ impl crate::app::App {
xmrig_is_alive: bool, xmrig_is_alive: bool,
xmrig_proxy_is_alive: bool, xmrig_proxy_is_alive: bool,
xvb_is_alive: bool, xvb_is_alive: bool,
xvb_is_running: bool,
) { ) {
// Middle panel, contents of the [Tab] // Middle panel, contents of the [Tab]
debug!("App | Rendering CENTRAL_PANEL (tab contents)"); debug!("App | Rendering CENTRAL_PANEL (tab contents)");
@ -43,12 +43,14 @@ impl crate::app::App {
let distro = true; let distro = true;
#[cfg(not(feature = "distro"))] #[cfg(not(feature = "distro"))]
let distro = false; let distro = false;
let node_gui_len = self.node_api.lock().unwrap().output.len();
let p2pool_gui_len = self.p2pool_api.lock().unwrap().output.len(); let p2pool_gui_len = self.p2pool_api.lock().unwrap().output.len();
let xmrig_gui_len = self.xmrig_api.lock().unwrap().output.len(); let xmrig_gui_len = self.xmrig_api.lock().unwrap().output.len();
let xmrig_proxy_gui_len = self.xmrig_proxy_api.lock().unwrap().output.len(); let xmrig_proxy_gui_len = self.xmrig_proxy_api.lock().unwrap().output.len();
let gupax_p2pool_api = self.gupax_p2pool_api.lock().unwrap(); let gupax_p2pool_api = self.gupax_p2pool_api.lock().unwrap();
let debug_info = format!( let debug_info = format!(
"Gupax version: {}\n "Gupax version: {}\n
Bundled Node version: {}\n
Bundled P2Pool version: {}\n Bundled P2Pool version: {}\n
Bundled XMRig version: {}\n Bundled XMRig version: {}\n
Bundled XMRig-Proxy version: {}\n Bundled XMRig-Proxy version: {}\n
@ -114,6 +116,7 @@ path_xmr: {:#?}\n
self.state.gupax.absolute_p2pool_path.display(), self.state.gupax.absolute_p2pool_path.display(),
self.state.gupax.absolute_xmrig_path.display(), self.state.gupax.absolute_xmrig_path.display(),
self.state.gupax.absolute_xp_path.display(), self.state.gupax.absolute_xp_path.display(),
node_gui_len,
p2pool_gui_len, p2pool_gui_len,
xmrig_gui_len, xmrig_gui_len,
xmrig_proxy_gui_len, xmrig_proxy_gui_len,
@ -181,7 +184,7 @@ path_xmr: {:#?}\n
} }
Tab::Xvb => { Tab::Xvb => {
debug!("App | Entering [XvB] Tab"); debug!("App | Entering [XvB] Tab");
crate::disk::state::Xvb::show(&mut self.state.xvb, self.size, &self.state.p2pool.address, ctx, ui, &self.xvb_api, &self.xmrig_api, self.xvb.lock().unwrap().state == ProcessState::Alive); crate::disk::state::Xvb::show(&mut self.state.xvb, self.size, &self.state.p2pool.address, ctx, ui, &self.xvb_api, &self.xmrig_api, xvb_is_running);
} }
} }
}); });

View file

@ -369,8 +369,12 @@ impl Update {
info!("Update | Saving state..."); info!("Update | Saving state...");
let original_version = og.lock().unwrap().version.clone(); let original_version = og.lock().unwrap().version.clone();
og.lock().unwrap().version = state_ver; og.lock().unwrap().version = state_ver;
match State::save(&mut og.lock().unwrap(), &state_path) { let mut state = og.lock().unwrap().to_owned();
Ok(_) => info!("Update ... OK"), match State::save(&mut state, &state_path) {
Ok(_) => {
info!("Update ... OK");
*og.lock().unwrap() = state;
}
Err(e) => { Err(e) => {
warn!("Update | Saving state ... FAIL: {}", e); warn!("Update | Saving state ... FAIL: {}", e);
og.lock().unwrap().version = original_version; og.lock().unwrap().version = original_version;

View file

@ -466,39 +466,39 @@ impl Helper {
// 2. Lock... EVERYTHING! // 2. Lock... EVERYTHING!
let mut lock = helper.lock().unwrap(); let mut lock = helper.lock().unwrap();
debug!("Helper | Locking (1/15) ... [helper]"); debug!("Helper | Locked (1/17) ... [helper]");
let node = node.lock().unwrap(); let node = node.lock().unwrap();
debug!("Helper | Locking (2/15) ... [helper]"); debug!("Helper | Locked (2/17) ... [node]");
let p2pool = p2pool.lock().unwrap(); let p2pool = p2pool.lock().unwrap();
debug!("Helper | Locking (3/15) ... [p2pool]"); debug!("Helper | Locked (3/17) ... [p2pool]");
let xmrig = xmrig.lock().unwrap(); let xmrig = xmrig.lock().unwrap();
debug!("Helper | Locking (4/15) ... [xmrig]"); debug!("Helper | Locked (4/17) ... [xmrig]");
let xmrig_proxy = xmrig_proxy.lock().unwrap(); let xmrig_proxy = xmrig_proxy.lock().unwrap();
debug!("Helper | Locking (5/15) ... [xmrig_proxy]"); debug!("Helper | Locked (5/17) ... [xmrig_proxy]");
let xvb = xvb.lock().unwrap(); let xvb = xvb.lock().unwrap();
debug!("Helper | Locking (6/15) ... [xvb]"); debug!("Helper | Locked (6/17) ... [xvb]");
let mut lock_pub_sys = pub_sys.lock().unwrap(); let mut lock_pub_sys = pub_sys.lock().unwrap();
debug!("Helper | Locking (8/15) ... [gui_api_node]"); debug!("Helper | Locked (8/17) ... [pub_sys]");
let mut gui_api_node = gui_api_node.lock().unwrap(); let mut gui_api_node = gui_api_node.lock().unwrap();
debug!("Helper | Locking (7/15) ... [pub_sys]"); debug!("Helper | Locked (7/17) ... [gui_api_node]");
let mut gui_api_p2pool = gui_api_p2pool.lock().unwrap(); let mut gui_api_p2pool = gui_api_p2pool.lock().unwrap();
debug!("Helper | Locking (8/15) ... [gui_api_p2pool]"); debug!("Helper | Locked (9/17) ... [gui_api_p2pool]");
let mut gui_api_xmrig = gui_api_xmrig.lock().unwrap(); let mut gui_api_xmrig = gui_api_xmrig.lock().unwrap();
debug!("Helper | Locking (9/15) ... [gui_api_xmrig]"); debug!("Helper | Locked (10/17) ... [gui_api_xmrig]");
let mut gui_api_xp = gui_api_xp.lock().unwrap(); let mut gui_api_xp = gui_api_xp.lock().unwrap();
debug!("Helper | Locking (10/15) ... [gui_api_xp]"); debug!("Helper | Locked (11/17) ... [gui_api_xp]");
let mut gui_api_xvb = gui_api_xvb.lock().unwrap(); let mut gui_api_xvb = gui_api_xvb.lock().unwrap();
debug!("Helper | Locking (11/15) ... [gui_api_xvb]"); debug!("Helper | Locked (12/17) ... [gui_api_xvb]");
let mut pub_api_node = pub_api_node.lock().unwrap(); let mut pub_api_node = pub_api_node.lock().unwrap();
debug!("Helper | Locking (14/15) ... [pub_api_node]"); debug!("Helper | Locked (13/17) ... [pub_api_node]");
let mut pub_api_p2pool = pub_api_p2pool.lock().unwrap(); let mut pub_api_p2pool = pub_api_p2pool.lock().unwrap();
debug!("Helper | Locking (14/15) ... [pub_api_p2pool]"); debug!("Helper | Locked (14/17) ... [pub_api_p2pool]");
let mut pub_api_xmrig = pub_api_xmrig.lock().unwrap(); let mut pub_api_xmrig = pub_api_xmrig.lock().unwrap();
debug!("Helper | Locking (13/15) ... [pub_api_xmrig]"); debug!("Helper | Locked (15/17) ... [pub_api_xmrig]");
let mut pub_api_xp = pub_api_xp.lock().unwrap(); let mut pub_api_xp = pub_api_xp.lock().unwrap();
debug!("Helper | Locking (14/15) ... [pub_api_xp]"); debug!("Helper | Locked (16/17) ... [pub_api_xp]");
let mut pub_api_xvb = pub_api_xvb.lock().unwrap(); let mut pub_api_xvb = pub_api_xvb.lock().unwrap();
debug!("Helper | Locking (15/15) ... [pub_api_xvb]"); debug!("Helper | Locked (17/17) ... [pub_api_xvb]");
// Calculate Gupax's uptime always. // Calculate Gupax's uptime always.
lock.uptime = HumanTime::into_human(lock.instant.elapsed()); lock.uptime = HumanTime::into_human(lock.instant.elapsed());
// If [Node] is alive... // If [Node] is alive...
@ -558,39 +558,39 @@ impl Helper {
// 3. Drop... (almost) EVERYTHING... IN REVERSE! // 3. Drop... (almost) EVERYTHING... IN REVERSE!
drop(lock_pub_sys); drop(lock_pub_sys);
debug!("Helper | Unlocking (1/12) ... [pub_sys]"); debug!("Helper | Unlocking (1/17) ... [pub_sys]");
drop(xvb); drop(xvb);
debug!("Helper | Unlocking (2/12) ... [xvb]"); debug!("Helper | Unlocking (2/17) ... [xvb]");
drop(xmrig_proxy); drop(xmrig_proxy);
debug!("Helper | Unlocking (3/12) ... [xmrig_proxy]"); debug!("Helper | Unlocking (3/17) ... [xmrig_proxy]");
drop(xmrig); drop(xmrig);
debug!("Helper | Unlocking (3/12) ... [xmrig]"); debug!("Helper | Unlocking (4/17) ... [xmrig]");
drop(p2pool); drop(p2pool);
debug!("Helper | Unlocking (4/12) ... [p2pool]"); debug!("Helper | Unlocking (5/17) ... [p2pool]");
drop(node); drop(node);
debug!("Helper | Unlocking (4/12) ... [node]"); debug!("Helper | Unlocking (6/17) ... [node]");
drop(pub_api_xvb); drop(pub_api_xvb);
debug!("Helper | Unlocking (5/12) ... [pub_api_xvb]"); debug!("Helper | Unlocking (7/17) ... [pub_api_xvb]");
drop(pub_api_xp); drop(pub_api_xp);
debug!("Helper | Unlocking (6/12) ... [pub_api_xp]"); debug!("Helper | Unlocking (8/17) ... [pub_api_xp]");
drop(pub_api_xmrig); drop(pub_api_xmrig);
debug!("Helper | Unlocking (6/12) ... [pub_api_xmrig]"); debug!("Helper | Unlocking (9/17) ... [pub_api_xmrig]");
drop(pub_api_p2pool); drop(pub_api_p2pool);
debug!("Helper | Unlocking (7/12) ... [pub_api_p2pool]"); debug!("Helper | Unlocking (10/17) ... [pub_api_p2pool]");
drop(pub_api_node); drop(pub_api_node);
debug!("Helper | Unlocking (7/12) ... [node]"); debug!("Helper | Unlocking (11/17) ... [pub_api_node]");
drop(gui_api_xvb); drop(gui_api_xvb);
debug!("Helper | Unlocking (8/12) ... [gui_api_xvb]"); debug!("Helper | Unlocking (12/17) ... [gui_api_xvb]");
drop(gui_api_xp); drop(gui_api_xp);
debug!("Helper | Unlocking (9/12) ... [gui_api_xp]"); debug!("Helper | Unlocking (13/17) ... [gui_api_xp]");
drop(gui_api_xmrig); drop(gui_api_xmrig);
debug!("Helper | Unlocking (10/12) ... [gui_api_xmrig]"); debug!("Helper | Unlocking (14/17) ... [gui_api_xmrig]");
drop(gui_api_p2pool); drop(gui_api_p2pool);
debug!("Helper | Unlocking (11/12) ... [gui_api_p2pool]"); debug!("Helper | Unlocking (15/17) ... [gui_api_p2pool]");
drop(gui_api_node); drop(gui_api_node);
debug!("Helper | Unlocking (11/12) ... [node]"); debug!("Helper | Unlocking (16/17) ... [gui_api_node]");
drop(lock); drop(lock);
debug!("Helper | Unlocking (12/12) ... [helper]"); debug!("Helper | Unlocking (17/17) ... [helper]");
// 4. Calculate if we should sleep or not. // 4. Calculate if we should sleep or not.
// If we should sleep, how long? // If we should sleep, how long?
@ -686,71 +686,63 @@ fn check_user_input(process: &Arc<Mutex<Process>>, stdin: &mut Box<dyn std::io::
} }
} }
fn signal_end( fn signal_end(
process: &Arc<Mutex<Process>>, process: &mut Process,
child_pty: &Arc<Mutex<Box<dyn Child + Sync + Send>>>, child_pty: &Arc<Mutex<Box<dyn Child + Sync + Send>>>,
start: &Instant, start: &Instant,
gui_api_output_raw: &mut String, gui_api_output_raw: &mut String,
) -> bool { ) -> bool {
if process.lock().unwrap().signal == ProcessSignal::Stop { let mut child_pty_lock = child_pty.lock().unwrap();
debug!("{} Watchdog | Stop SIGNAL caught", process.lock().unwrap().name); if process.signal == ProcessSignal::Stop {
debug!("{} Watchdog | Stop SIGNAL caught", process.name);
// This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool) // This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool)
if let Err(e) = child_pty.lock().unwrap().kill() { if let Err(e) = child_pty_lock.kill() {
error!("{} Watchdog | Kill error: {}", process.lock().unwrap().name, e); error!("{} Watchdog | Kill error: {}", process.name, e);
} }
// Wait to get the exit status // Wait to get the exit status
let exit_status = match child_pty.lock().unwrap().wait() { let exit_status = match child_pty_lock.wait() {
Ok(e) => { Ok(e) => {
if e.success() { if e.success() {
process.lock().unwrap().state = ProcessState::Dead; process.state = ProcessState::Dead;
"Successful" "Successful"
} else { } else {
process.lock().unwrap().state = ProcessState::Failed; process.state = ProcessState::Failed;
"Failed" "Failed"
} }
} }
_ => { _ => {
process.lock().unwrap().state = ProcessState::Failed; process.state = ProcessState::Failed;
"Unknown Error" "Unknown Error"
} }
}; };
let uptime = HumanTime::into_human(start.elapsed()); let uptime = HumanTime::into_human(start.elapsed());
info!( info!(
"{} Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]", "{} Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]",
process.lock().unwrap().name, process.name, uptime, exit_status
uptime,
exit_status
); );
// This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it. // This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it.
let name = process.name.to_owned();
if let Err(e) = writeln!( if let Err(e) = writeln!(
gui_api_output_raw, gui_api_output_raw,
"{}\n{} stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", "{}\n{} stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
process.lock().unwrap().name, name, HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE
HORI_CONSOLE,
uptime,
exit_status,
HORI_CONSOLE
) { ) {
error!( error!(
"{} Watchdog | GUI Uptime/Exit status write failed: {}", "{} Watchdog | GUI Uptime/Exit status write failed: {}",
process.lock().unwrap().name, name, e
e
); );
} }
process.lock().unwrap().signal = ProcessSignal::None; process.signal = ProcessSignal::None;
debug!( debug!("{} Watchdog | Stop SIGNAL done, breaking", process.name,);
"{} Watchdog | Stop SIGNAL done, breaking",
process.lock().unwrap().name,
);
return true; return true;
// Check RESTART // Check RESTART
} else if process.lock().unwrap().signal == ProcessSignal::Restart { } else if process.signal == ProcessSignal::Restart {
debug!("{} Watchdog | Restart SIGNAL caught", process.lock().unwrap().name,); debug!("{} Watchdog | Restart SIGNAL caught", process.name,);
// This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool) // This actually sends a SIGHUP to p2pool (closes the PTY, hangs up on p2pool)
if let Err(e) = child_pty.lock().unwrap().kill() { if let Err(e) = child_pty_lock.kill() {
error!("{} Watchdog | Kill error: {}", process.lock().unwrap().name, e); error!("{} Watchdog | Kill error: {}", process.name, e);
} }
// Wait to get the exit status // Wait to get the exit status
let exit_status = match child_pty.lock().unwrap().wait() { let exit_status = match child_pty_lock.wait() {
Ok(e) => { Ok(e) => {
if e.success() { if e.success() {
"Successful" "Successful"
@ -763,31 +755,22 @@ fn signal_end(
let uptime = HumanTime::into_human(start.elapsed()); let uptime = HumanTime::into_human(start.elapsed());
info!( info!(
"{} Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]", "{} Watchdog | Stopped ... Uptime was: [{}], Exit status: [{}]",
process.lock().unwrap().name, process.name, uptime, exit_status
uptime,
exit_status
); );
// This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it. // This is written directly into the GUI API, because sometimes the 900ms event loop can't catch it.
let name = process.name.to_owned();
if let Err(e) = writeln!( if let Err(e) = writeln!(
gui_api_output_raw, gui_api_output_raw,
"{}\n{} stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n", "{}\n{} stopped | Uptime: [{}] | Exit status: [{}]\n{}\n\n\n\n",
process.lock().unwrap().name, name, HORI_CONSOLE, uptime, exit_status, HORI_CONSOLE
HORI_CONSOLE,
uptime,
exit_status,
HORI_CONSOLE
) { ) {
error!( error!(
"{} Watchdog | GUI Uptime/Exit status write failed: {}", "{} Watchdog | GUI Uptime/Exit status write failed: {}",
process.lock().unwrap().name, name, e
e
); );
} }
process.lock().unwrap().state = ProcessState::Waiting; process.state = ProcessState::Waiting;
debug!( debug!("{} Watchdog | Restart SIGNAL done, breaking", process.name,);
"{} Watchdog | Restart SIGNAL done, breaking",
process.lock().unwrap().name,
);
return true; return true;
} }
false false
@ -796,8 +779,8 @@ async fn sleep_end_loop(now: Instant, name: ProcessName) {
// Sleep (only if 999ms hasn't passed) // Sleep (only if 999ms hasn't passed)
let elapsed = now.elapsed().as_millis(); let elapsed = now.elapsed().as_millis();
// Since logic goes off if less than 1000, casting should be safe // Since logic goes off if less than 1000, casting should be safe
if elapsed < 999 { if elapsed < 1000 {
let sleep = (999 - elapsed) as u64; let sleep = (1000 - elapsed) as u64;
debug!( debug!(
"{} Watchdog | END OF LOOP - Sleeping for [{}]ms...", "{} Watchdog | END OF LOOP - Sleeping for [{}]ms...",
name, sleep name, sleep

View file

@ -227,56 +227,62 @@ impl Helper {
loop { loop {
let now = Instant::now(); let now = Instant::now();
debug!("Node Watchdog | ----------- Start of loop -----------"); debug!("Node Watchdog | ----------- Start of loop -----------");
// check state
if check_died(
&child_pty,
&mut process.lock().unwrap(),
&start,
&mut gui_api.lock().unwrap().output,
) {
break;
}
// check signal
if signal_end(
process,
&child_pty,
&start,
&mut gui_api.lock().unwrap().output,
) {
break;
}
// check user input
check_user_input(process, &mut stdin);
// get data output/api
// Check if logs need resetting
debug!("Node Watchdog | Attempting GUI log reset check");
{ {
let mut lock = gui_api.lock().unwrap(); // scope to drop locked mutex before the sleep
Self::check_reset_gui_output(&mut lock.output, ProcessName::Node); // check state
} if check_died(
// No need to check output since monerod has a sufficient API &child_pty,
// Always update from output &mut process.lock().unwrap(),
debug!("Node Watchdog | Starting [update_from_output()]"); &start,
PubNodeApi::update_from_output(pub_api, &output_pub, start.elapsed()); &mut gui_api.lock().unwrap().output,
// update data from api ) {
debug!("Node Watchdog | Attempting HTTP API request..."); break;
match PrivNodeApi::request_api(&client, &state).await {
Ok(priv_api) => {
debug!("Node Watchdog | HTTP API request OK, attempting [update_from_priv()]");
if priv_api.result.synchronized && priv_api.result.status == "OK" {
process.lock().unwrap().state = ProcessState::Alive
}
PubNodeApi::update_from_priv(pub_api, priv_api);
} }
Err(err) => { // check signal
// if node is just starting, do not throw an error if signal_end(
if start.elapsed() > Duration::from_secs(10) { &mut process.lock().unwrap(),
warn!( &child_pty,
"Node Watchdog | Could not send HTTP API request to node\n{}", &start,
err &mut gui_api.lock().unwrap().output,
) {
break;
}
// check user input
check_user_input(process, &mut stdin);
// get data output/api
// Check if logs need resetting
debug!("Node Watchdog | Attempting GUI log reset check");
{
Self::check_reset_gui_output(
&mut gui_api.lock().unwrap().output,
ProcessName::Node,
);
}
// No need to check output since monerod has a sufficient API
// Always update from output
debug!("Node Watchdog | Starting [update_from_output()]");
PubNodeApi::update_from_output(pub_api, &output_pub, start.elapsed());
// update data from api
debug!("Node Watchdog | Attempting HTTP API request...");
match PrivNodeApi::request_api(&client, &state).await {
Ok(priv_api) => {
debug!(
"Node Watchdog | HTTP API request OK, attempting [update_from_priv()]"
); );
if priv_api.result.synchronized && priv_api.result.status == "OK" {
process.lock().unwrap().state = ProcessState::Alive
}
PubNodeApi::update_from_priv(pub_api, priv_api);
}
Err(err) => {
// if node is just starting, do not throw an error
if start.elapsed() > Duration::from_secs(10) {
warn!(
"Node Watchdog | Could not send HTTP API request to node\n{}",
err
);
}
} }
} }
} }

View file

@ -527,109 +527,111 @@ impl Helper {
// Set timer // Set timer
let now = Instant::now(); let now = Instant::now();
debug!("P2Pool Watchdog | ----------- Start of loop -----------"); debug!("P2Pool Watchdog | ----------- Start of loop -----------");
gui_api.lock().unwrap().tick = (last_p2pool_request.elapsed().as_secs() % 60) as u8;
// Check if the process is secretly died without us knowing :)
if check_died(
&child_pty,
&mut process.lock().unwrap(),
&start,
&mut gui_api.lock().unwrap().output,
) {
break;
}
// Check SIGNAL
if signal_end(
&process,
&child_pty,
&start,
&mut gui_api.lock().unwrap().output,
) {
break;
}
// Check vector of user input
check_user_input(&process, &mut stdin);
// Check if logs need resetting
debug!("P2Pool Watchdog | Attempting GUI log reset check");
let mut lock = gui_api.lock().unwrap();
Self::check_reset_gui_output(&mut lock.output, ProcessName::P2pool);
drop(lock);
// Always update from output
debug!("P2Pool Watchdog | Starting [update_from_output()]");
PubP2poolApi::update_from_output(
&pub_api,
&output_parse,
&output_pub,
start.elapsed(),
&process,
);
// Read [local] API
debug!("P2Pool Watchdog | Attempting [local] API file read");
if let Ok(string) = Self::path_to_string(&api_path_local, ProcessName::P2pool) {
// Deserialize
if let Ok(local_api) = PrivP2poolLocalApi::from_str(&string) {
// Update the structs.
PubP2poolApi::update_from_local(&pub_api, local_api);
}
}
// If more than 1 minute has passed, read the other API files.
let last_p2pool_request_expired =
last_p2pool_request.elapsed() >= Duration::from_secs(60);
// need to reload fast to get the first right values after syncing.
// check if value is 100k or under and request immediately if that's the case. fixed in release of p2pool including commit https://github.com/SChernykh/p2pool/commit/64a199be6dec7924b41f857a401086f25e1ec9be
if (last_p2pool_request_expired
|| pub_api.lock().unwrap().p2pool_difficulty_u64 <= 100000)
&& process.lock().unwrap().state == ProcessState::Alive
{ {
debug!("P2Pool Watchdog | Attempting [network] & [pool] API file read"); gui_api.lock().unwrap().tick = (last_p2pool_request.elapsed().as_secs() % 60) as u8;
if let (Ok(network_api), Ok(pool_api)) = ( // Check if the process is secretly died without us knowing :)
Self::path_to_string(&api_path_network, ProcessName::P2pool), if check_died(
Self::path_to_string(&api_path_pool, ProcessName::P2pool), &child_pty,
&mut process.lock().unwrap(),
&start,
&mut gui_api.lock().unwrap().output,
) { ) {
if let (Ok(network_api), Ok(pool_api)) = ( break;
PrivP2poolNetworkApi::from_str(&network_api), }
PrivP2poolPoolApi::from_str(&pool_api),
) { // Check SIGNAL
PubP2poolApi::update_from_network_pool(&pub_api, network_api, pool_api); if signal_end(
last_p2pool_request = tokio::time::Instant::now(); &mut process.lock().unwrap(),
&child_pty,
&start,
&mut gui_api.lock().unwrap().output,
) {
break;
}
// Check vector of user input
check_user_input(&process, &mut stdin);
// Check if logs need resetting
debug!("P2Pool Watchdog | Attempting GUI log reset check");
let mut lock = gui_api.lock().unwrap();
Self::check_reset_gui_output(&mut lock.output, ProcessName::P2pool);
drop(lock);
// Always update from output
debug!("P2Pool Watchdog | Starting [update_from_output()]");
let mut process_lock = process.lock().unwrap();
let mut pub_api_lock = pub_api.lock().unwrap();
PubP2poolApi::update_from_output(
&mut pub_api_lock,
&output_parse,
&output_pub,
start.elapsed(),
&mut process_lock,
);
// Read [local] API
debug!("P2Pool Watchdog | Attempting [local] API file read");
if let Ok(string) = Self::path_to_string(&api_path_local, ProcessName::P2pool) {
// Deserialize
if let Ok(local_api) = PrivP2poolLocalApi::from_str(&string) {
// Update the structs.
PubP2poolApi::update_from_local(&mut pub_api_lock, local_api);
} }
} }
} // If more than 1 minute has passed, read the other API files.
let last_p2pool_request_expired =
let last_status_request_expired = last_p2pool_request.elapsed() >= Duration::from_secs(60);
last_status_request.elapsed() >= Duration::from_secs(60); // need to reload fast to get the first right values after syncing.
// check if value is 100k or under and request immediately if that's the case. fixed in release of p2pool including commit https://github.com/SChernykh/p2pool/commit/64a199be6dec7924b41f857a401086f25e1ec9be
if (last_status_request_expired || first_loop) if (last_p2pool_request_expired
&& process.lock().unwrap().state == ProcessState::Alive || pub_api.lock().unwrap().p2pool_difficulty_u64 <= 100000)
{ && process_lock.state == ProcessState::Alive
debug!("P2Pool Watchdog | Reading status output of p2pool node"); {
#[cfg(target_os = "windows")] debug!("P2Pool Watchdog | Attempting [network] & [pool] API file read");
if let Err(e) = write!(stdin, "statusfromgupaxx\r\n") { if let (Ok(network_api), Ok(pool_api)) = (
error!("P2Pool Watchdog | STDIN error: {}", e); Self::path_to_string(&api_path_network, ProcessName::P2pool),
Self::path_to_string(&api_path_pool, ProcessName::P2pool),
) {
if let (Ok(network_api), Ok(pool_api)) = (
PrivP2poolNetworkApi::from_str(&network_api),
PrivP2poolPoolApi::from_str(&pool_api),
) {
PubP2poolApi::update_from_network_pool(
&mut pub_api_lock,
network_api,
pool_api,
);
last_p2pool_request = tokio::time::Instant::now();
}
}
} }
#[cfg(target_family = "unix")]
if let Err(e) = writeln!(stdin, "statusfromgupaxx") {
error!("P2Pool Watchdog | STDIN error: {}", e);
}
// Flush.
if let Err(e) = stdin.flush() {
error!("P2Pool Watchdog | STDIN flush error: {}", e);
}
last_status_request = tokio::time::Instant::now();
}
// Sleep (only if 900ms hasn't passed) let last_status_request_expired =
if first_loop { last_status_request.elapsed() >= Duration::from_secs(60);
first_loop = false; if (last_status_request_expired || first_loop)
} && process_lock.state == ProcessState::Alive
{
debug!("P2Pool Watchdog | Reading status output of p2pool node");
#[cfg(target_os = "windows")]
if let Err(e) = write!(stdin, "statusfromgupaxx\r\n") {
error!("P2Pool Watchdog | STDIN error: {}", e);
}
#[cfg(target_family = "unix")]
if let Err(e) = writeln!(stdin, "statusfromgupaxx") {
error!("P2Pool Watchdog | STDIN error: {}", e);
}
// Flush.
if let Err(e) = stdin.flush() {
error!("P2Pool Watchdog | STDIN flush error: {}", e);
}
last_status_request = tokio::time::Instant::now();
}
// Sleep (only if 900ms hasn't passed)
if first_loop {
first_loop = false;
}
} // end of scope to drop lock
sleep_end_loop(now, ProcessName::P2pool).await; sleep_end_loop(now, ProcessName::P2pool).await;
debug!(
"P2Pool Watchdog | END OF LOOP - Tick: [{}/60]",
gui_api.lock().unwrap().tick,
);
} }
// 5. If loop broke, we must be done here. // 5. If loop broke, we must be done here.
@ -826,46 +828,39 @@ impl PubP2poolApi {
// Mutate "watchdog"'s [PubP2poolApi] with data the process output. // Mutate "watchdog"'s [PubP2poolApi] with data the process output.
pub(super) fn update_from_output( pub(super) fn update_from_output(
public: &Arc<Mutex<Self>>, public: &mut Self,
output_parse: &Arc<Mutex<String>>, output_parse: &Arc<Mutex<String>>,
output_pub: &Arc<Mutex<String>>, output_pub: &Arc<Mutex<String>>,
elapsed: std::time::Duration, elapsed: std::time::Duration,
process: &Arc<Mutex<Process>>, process: &mut Process,
) { ) {
// 1. Take the process's current output buffer and combine it with Pub (if not empty) // 1. Take the process's current output buffer and combine it with Pub (if not empty)
let mut output_pub = output_pub.lock().unwrap(); let mut output_pub = output_pub.lock().unwrap();
if !output_pub.is_empty() { if !output_pub.is_empty() {
public public.output.push_str(&std::mem::take(&mut *output_pub));
.lock()
.unwrap()
.output
.push_str(&std::mem::take(&mut *output_pub));
} }
// 2. Parse the full STDOUT // 2. Parse the full STDOUT
let mut output_parse = output_parse.lock().unwrap(); let mut output_parse = output_parse.lock().unwrap();
let (payouts_new, xmr_new) = Self::calc_payouts_and_xmr(&output_parse); let (payouts_new, xmr_new) = Self::calc_payouts_and_xmr(&output_parse);
// Check for "SYNCHRONIZED" only if we aren't already. // Check for "SYNCHRONIZED" only if we aren't already.
if process.lock().unwrap().state == ProcessState::Syncing { if process.state == ProcessState::Syncing {
// look for depth 0 // look for depth 0
if P2POOL_REGEX.depth_0.is_match(&output_parse) { if P2POOL_REGEX.depth_0.is_match(&output_parse) {
process.lock().unwrap().state = ProcessState::Alive; process.state = ProcessState::Alive;
} }
} }
// check if zmq server still alive // check if zmq server still alive
if process.lock().unwrap().state == ProcessState::Alive if process.state == ProcessState::Alive && contains_zmq_connection_lost(&output_parse) {
&& contains_zmq_connection_lost(&output_parse)
{
// node zmq is not responding, p2pool is not ready // node zmq is not responding, p2pool is not ready
process.lock().unwrap().state = ProcessState::Syncing; process.state = ProcessState::Syncing;
} }
// 3. Throw away [output_parse] // 3. Throw away [output_parse]
output_parse.clear(); output_parse.clear();
drop(output_parse); drop(output_parse);
// 4. Add to current values // 4. Add to current values
let mut public = public.lock().unwrap();
let (payouts, xmr) = (public.payouts + payouts_new, public.xmr + xmr_new); let (payouts, xmr) = (public.payouts + payouts_new, public.xmr + xmr_new);
// 5. Calculate hour/day/month given elapsed time // 5. Calculate hour/day/month given elapsed time
@ -912,13 +907,12 @@ impl PubP2poolApi {
xmr_hour, xmr_hour,
xmr_day, xmr_day,
xmr_month, xmr_month,
..std::mem::take(&mut *public) ..std::mem::take(public)
}; };
} }
// Mutate [PubP2poolApi] with data from a [PrivP2poolLocalApi] and the process output. // Mutate [PubP2poolApi] with data from a [PrivP2poolLocalApi] and the process output.
pub(super) fn update_from_local(public: &Arc<Mutex<Self>>, local: PrivP2poolLocalApi) { pub(super) fn update_from_local(public: &mut Self, local: PrivP2poolLocalApi) {
let mut public = public.lock().unwrap();
*public = Self { *public = Self {
hashrate_15m: HumanNumber::from_u64(local.hashrate_15m), hashrate_15m: HumanNumber::from_u64(local.hashrate_15m),
hashrate_1h: HumanNumber::from_u64(local.hashrate_1h), hashrate_1h: HumanNumber::from_u64(local.hashrate_1h),
@ -934,11 +928,11 @@ impl PubP2poolApi {
// Mutate [PubP2poolApi] with data from a [PrivP2pool(Network|Pool)Api]. // Mutate [PubP2poolApi] with data from a [PrivP2pool(Network|Pool)Api].
pub(super) fn update_from_network_pool( pub(super) fn update_from_network_pool(
public: &Arc<Mutex<Self>>, public: &mut Self,
net: PrivP2poolNetworkApi, net: PrivP2poolNetworkApi,
pool: PrivP2poolPoolApi, pool: PrivP2poolPoolApi,
) { ) {
let user_hashrate = public.lock().unwrap().user_p2pool_hashrate_u64; // The user's total P2Pool hashrate let user_hashrate = public.user_p2pool_hashrate_u64; // The user's total P2Pool hashrate
let monero_difficulty = net.difficulty; let monero_difficulty = net.difficulty;
let monero_hashrate = monero_difficulty / MONERO_BLOCK_TIME_IN_SECONDS; let monero_hashrate = monero_difficulty / MONERO_BLOCK_TIME_IN_SECONDS;
let p2pool_hashrate = pool.pool_statistics.hashRate; let p2pool_hashrate = pool.pool_statistics.hashRate;
@ -980,7 +974,6 @@ impl PubP2poolApi {
p2pool_difficulty / user_hashrate, p2pool_difficulty / user_hashrate,
)); ));
} }
let mut public = public.lock().unwrap();
*public = Self { *public = Self {
p2pool_difficulty_u64: p2pool_difficulty, p2pool_difficulty_u64: p2pool_difficulty,
monero_difficulty_u64: monero_difficulty, monero_difficulty_u64: monero_difficulty,

View file

@ -116,8 +116,14 @@ Uptime = 0h 2m 4s
"".to_string(), "".to_string(),
PathBuf::new(), PathBuf::new(),
))); )));
PubP2poolApi::update_from_output(&public, &output_parse, &output_pub, elapsed, &process); let mut public = public.lock().unwrap();
let public = public.lock().unwrap(); PubP2poolApi::update_from_output(
&mut public,
&output_parse,
&output_pub,
elapsed,
&mut process.lock().unwrap(),
);
println!("{:#?}", public); println!("{:#?}", public);
assert_eq!(public.payouts, 3); assert_eq!(public.payouts, 3);
assert_eq!(public.payouts_hour, 180.0); assert_eq!(public.payouts_hour, 180.0);
@ -161,8 +167,8 @@ Uptime = 0h 2m 4s
}, },
}; };
// Update Local // Update Local
PubP2poolApi::update_from_local(&public, local); let mut p = public.lock().unwrap();
let p = public.lock().unwrap(); PubP2poolApi::update_from_local(&mut p, local);
println!("AFTER LOCAL: {:#?}", p); println!("AFTER LOCAL: {:#?}", p);
assert_eq!(p.hashrate_15m.to_string(), "10,000"); assert_eq!(p.hashrate_15m.to_string(), "10,000");
assert_eq!(p.hashrate_1h.to_string(), "20,000"); assert_eq!(p.hashrate_1h.to_string(), "20,000");
@ -175,10 +181,8 @@ Uptime = 0h 2m 4s
assert_eq!(p.current_effort.to_string(), "200.00%"); assert_eq!(p.current_effort.to_string(), "200.00%");
assert_eq!(p.connections.to_string(), "1,234"); assert_eq!(p.connections.to_string(), "1,234");
assert_eq!(p.user_p2pool_hashrate_u64, 20000); assert_eq!(p.user_p2pool_hashrate_u64, 20000);
drop(p);
// Update Network + Pool // Update Network + Pool
PubP2poolApi::update_from_network_pool(&public, network, pool); PubP2poolApi::update_from_network_pool(&mut p, network, pool);
let p = public.lock().unwrap();
println!("AFTER NETWORK+POOL: {:#?}", p); println!("AFTER NETWORK+POOL: {:#?}", p);
assert_eq!(p.monero_difficulty.to_string(), "300,000,000,000"); assert_eq!(p.monero_difficulty.to_string(), "300,000,000,000");
assert_eq!(p.monero_hashrate.to_string(), "2.500 GH/s"); assert_eq!(p.monero_hashrate.to_string(), "2.500 GH/s");

View file

@ -1,12 +1,12 @@
use crate::constants::*;
use crate::helper::xrig::update_xmrig_config; use crate::helper::xrig::update_xmrig_config;
use crate::helper::{check_died, check_user_input, sleep_end_loop, Process}; use crate::helper::{arc_mut, check_died, check_user_input, sleep, sleep_end_loop, Process};
use crate::helper::{Helper, ProcessName, ProcessSignal, ProcessState}; use crate::helper::{Helper, ProcessName, ProcessSignal, ProcessState};
use crate::helper::{PubXvbApi, XvbNode}; use crate::helper::{PubXvbApi, XvbNode};
use crate::miscs::output_console; use crate::miscs::output_console;
use crate::regex::{contains_error, contains_usepool, detect_new_node_xmrig, XMRIG_REGEX}; use crate::regex::{contains_error, contains_usepool, detect_new_node_xmrig, XMRIG_REGEX};
use crate::utils::human::HumanNumber; use crate::utils::human::HumanNumber;
use crate::utils::sudo::SudoState; use crate::utils::sudo::SudoState;
use crate::{constants::*, macros::*};
use enclose::enclose; use enclose::enclose;
use log::*; use log::*;
use portable_pty::Child; use portable_pty::Child;
@ -514,7 +514,7 @@ impl Helper {
} }
// Stop on [Stop/Restart] SIGNAL // Stop on [Stop/Restart] SIGNAL
if Self::xmrig_signal_end( if Self::xmrig_signal_end(
&process, &mut process.lock().unwrap(),
&child_pty, &child_pty,
&start, &start,
&mut gui_api.lock().unwrap().output, &mut gui_api.lock().unwrap().output,
@ -591,13 +591,13 @@ impl Helper {
info!("XMRig Watchdog | Watchdog thread exiting... Goodbye!"); info!("XMRig Watchdog | Watchdog thread exiting... Goodbye!");
} }
fn xmrig_signal_end( fn xmrig_signal_end(
process: &Arc<Mutex<Process>>, process: &mut Process,
child_pty: &Arc<Mutex<Box<dyn Child + Sync + Send>>>, child_pty: &Arc<Mutex<Box<dyn Child + Sync + Send>>>,
start: &Instant, start: &Instant,
gui_api_output_raw: &mut String, gui_api_output_raw: &mut String,
sudo: &Arc<Mutex<SudoState>>, sudo: &Arc<Mutex<SudoState>>,
) -> bool { ) -> bool {
let signal = process.lock().unwrap().signal; let signal = process.signal;
if signal == ProcessSignal::Stop || signal == ProcessSignal::Restart { if signal == ProcessSignal::Stop || signal == ProcessSignal::Restart {
debug!("XMRig Watchdog | Stop/Restart SIGNAL caught"); debug!("XMRig Watchdog | Stop/Restart SIGNAL caught");
// macOS requires [sudo] again to kill [XMRig] // macOS requires [sudo] again to kill [XMRig]
@ -616,7 +616,6 @@ impl Helper {
} }
let exit_status = match child_pty.lock().unwrap().wait() { let exit_status = match child_pty.lock().unwrap().wait() {
Ok(e) => { Ok(e) => {
let mut process = process.lock().unwrap();
if e.success() { if e.success() {
if process.signal == ProcessSignal::Stop { if process.signal == ProcessSignal::Stop {
process.state = ProcessState::Dead; process.state = ProcessState::Dead;
@ -630,7 +629,6 @@ impl Helper {
} }
} }
_ => { _ => {
let mut process = process.lock().unwrap();
if process.signal == ProcessSignal::Stop { if process.signal == ProcessSignal::Stop {
process.state = ProcessState::Failed; process.state = ProcessState::Failed;
} }
@ -652,7 +650,6 @@ impl Helper {
e e
); );
} }
let mut process = process.lock().unwrap();
match process.signal { match process.signal {
ProcessSignal::Stop => process.signal = ProcessSignal::None, ProcessSignal::Stop => process.signal = ProcessSignal::None,
ProcessSignal::Restart => process.state = ProcessState::Waiting, ProcessSignal::Restart => process.state = ProcessState::Waiting,

View file

@ -371,91 +371,93 @@ impl Helper {
loop { loop {
let now = Instant::now(); let now = Instant::now();
debug!("XMRig-Proxy Watchdog | ----------- Start of loop -----------"); debug!("XMRig-Proxy Watchdog | ----------- Start of loop -----------");
// check state {
if check_died( if check_died(
&child_pty, &child_pty,
&mut process.lock().unwrap(), &mut process.lock().unwrap(),
&start, &start,
&mut gui_api.lock().unwrap().output, &mut gui_api.lock().unwrap().output,
) { ) {
break; break;
} }
// check signal // check signal
if signal_end( if signal_end(
process, &mut process.lock().unwrap(),
&child_pty, &child_pty,
&start, &start,
&mut gui_api.lock().unwrap().output, &mut gui_api.lock().unwrap().output,
) { ) {
break; break;
} }
// check user input // check user input
check_user_input(process, &mut stdin); check_user_input(process, &mut stdin);
// get data output/api // get data output/api
// Check if logs need resetting // Check if logs need resetting
debug!("XMRig-Proxy Watchdog | Attempting GUI log reset check"); debug!("XMRig-Proxy Watchdog | Attempting GUI log reset check");
{ Self::check_reset_gui_output(
let mut lock = gui_api.lock().unwrap(); &mut gui_api.lock().unwrap().output,
Self::check_reset_gui_output(&mut lock.output, ProcessName::XmrigProxy); ProcessName::XmrigProxy,
} );
// Always update from output // Always update from output
// todo: check difference with xmrig // todo: check difference with xmrig
debug!("XMRig-Proxy Watchdog | Starting [update_from_output()]"); debug!("XMRig-Proxy Watchdog | Starting [update_from_output()]");
PubXmrigProxyApi::update_from_output( PubXmrigProxyApi::update_from_output(
pub_api, pub_api,
&output_pub, &output_pub,
&output_parse, &output_parse,
start.elapsed(), start.elapsed(),
process, process,
); );
// update data from api // update data from api
debug!("XMRig-Proxy Watchdog | Attempting HTTP API request..."); debug!("XMRig-Proxy Watchdog | Attempting HTTP API request...");
match PrivXmrigProxyApi::request_xp_api(&client, api_summary_xp, token_proxy).await { match PrivXmrigProxyApi::request_xp_api(&client, api_summary_xp, token_proxy).await
Ok(priv_api) => {
debug!("XMRig-Proxy Watchdog | HTTP API request OK, attempting [update_from_priv()]");
PubXmrigProxyApi::update_from_priv(pub_api, priv_api);
}
Err(err) => {
warn!(
"XMRig-Proxy Watchdog | Could not send HTTP API request to: {}\n{}",
api_summary_xp, err
);
}
}
// update xmrig to use xmrig-proxy if option enabled and local xmrig alive
if xmrig_redirect
&& gui_api_xmrig.lock().unwrap().node != XvbNode::XmrigProxy.to_string()
&& (process_xmrig.lock().unwrap().state == ProcessState::Alive
|| process_xmrig.lock().unwrap().state == ProcessState::NotMining)
{
info!("redirect local xmrig instance to xmrig-proxy");
if let Err(err) = update_xmrig_config(
&client,
api_config_xmrig,
&state_xmrig.token,
&XvbNode::XmrigProxy,
"",
GUPAX_VERSION_UNDERSCORE,
)
.await
{ {
// show to console error about updating xmrig config Ok(priv_api) => {
warn!("XMRig-Proxy Process | Failed request HTTP API Xmrig"); debug!("XMRig-Proxy Watchdog | HTTP API request OK, attempting [update_from_priv()]");
output_console( PubXmrigProxyApi::update_from_priv(pub_api, priv_api);
&mut gui_api.lock().unwrap().output, }
&format!( Err(err) => {
"Failure to update xmrig config with HTTP API.\nError: {}", warn!(
err "XMRig-Proxy Watchdog | Could not send HTTP API request to: {}\n{}",
), api_summary_xp, err
ProcessName::XmrigProxy, );
); }
} else {
gui_api_xmrig.lock().unwrap().node = XvbNode::XmrigProxy.to_string();
debug!("XMRig-Proxy Process | mining on Xmrig-Proxy pool");
} }
} // update xmrig to use xmrig-proxy if option enabled and local xmrig alive
// do not use more than 1 second for the loop if xmrig_redirect
&& gui_api_xmrig.lock().unwrap().node != XvbNode::XmrigProxy.to_string()
&& (process_xmrig.lock().unwrap().state == ProcessState::Alive
|| process_xmrig.lock().unwrap().state == ProcessState::NotMining)
{
info!("redirect local xmrig instance to xmrig-proxy");
if let Err(err) = update_xmrig_config(
&client,
api_config_xmrig,
&state_xmrig.token,
&XvbNode::XmrigProxy,
"",
GUPAX_VERSION_UNDERSCORE,
)
.await
{
// show to console error about updating xmrig config
warn!("XMRig-Proxy Process | Failed request HTTP API Xmrig");
output_console(
&mut gui_api.lock().unwrap().output,
&format!(
"Failure to update xmrig config with HTTP API.\nError: {}",
err
),
ProcessName::XmrigProxy,
);
} else {
gui_api_xmrig.lock().unwrap().node = XvbNode::XmrigProxy.to_string();
debug!("XMRig-Proxy Process | mining on Xmrig-Proxy pool");
}
}
} // locked are dropped here
// do not use more than 1 second for the loop
sleep_end_loop(now, ProcessName::XmrigProxy).await; sleep_end_loop(now, ProcessName::XmrigProxy).await;
} }

View file

@ -208,203 +208,207 @@ impl Helper {
debug!("XvB Watchdog | ----------- Start of loop -----------"); debug!("XvB Watchdog | ----------- Start of loop -----------");
// Set timer of loop // Set timer of loop
let start_loop = std::time::Instant::now(); let start_loop = std::time::Instant::now();
// check if first loop the state of Xmrig-Proxy
if first_loop {
xp_alive = process_xp.lock().unwrap().state == ProcessState::Alive;
msg_retry_done = false;
*retry.lock().unwrap() = false;
}
// verify if p2pool and xmrig are running, else XvB must be reloaded with another token/address to start verifying the other process.
if check_state_outcauses_xvb(
&client,
gui_api,
pub_api,
process,
process_xmrig,
process_xp,
process_p2pool,
&mut first_loop,
&handle_algo,
pub_api_xmrig,
pub_api_xp,
state_p2pool,
state_xmrig,
state_xp,
xp_alive,
)
.await
{ {
continue; // check if first loop the state of Xmrig-Proxy
} if first_loop {
// check signal xp_alive = process_xp.lock().unwrap().state == ProcessState::Alive;
debug!("XvB | check signal"); msg_retry_done = false;
if signal_interrupt( *retry.lock().unwrap() = false;
process, }
if xp_alive { process_xp } else { process_xmrig }, // verify if p2pool and xmrig are running, else XvB must be reloaded with another token/address to start verifying the other process.
start.into(), if check_state_outcauses_xvb(
&client, &client,
pub_api, gui_api,
gui_api, pub_api,
gui_api_xmrig, process,
gui_api_xp, process_xmrig,
state_p2pool, process_xp,
state_xmrig, process_p2pool,
state_xp, &mut first_loop,
xp_alive, &handle_algo,
) { pub_api_xmrig,
info!("XvB Watchdog | Signal has stopped the loop"); pub_api_xp,
break; state_p2pool,
} state_xmrig,
// let handle_algo_c = handle_algo.lock().unwrap(); state_xp,
let is_algo_started_once = handle_algo.lock().unwrap().is_some(); xp_alive,
let is_algo_finished = handle_algo )
.lock() .await
.unwrap() {
.as_ref() continue;
.is_some_and(|algo| algo.is_finished()); }
let is_request_finished = handle_request // check signal
.lock() debug!("XvB | check signal");
.unwrap() if signal_interrupt(
.as_ref() process,
.is_some_and(|request: &JoinHandle<()>| request.is_finished()) if xp_alive { process_xp } else { process_xmrig },
|| handle_request.lock().unwrap().is_none(); start.into(),
// Send an HTTP API request only if one minute is passed since the last request or if first loop or if algorithm need to retry or if request is finished and algo is finished or almost finished (only public and private stats). We make sure public and private stats are refreshed before doing another run of the algo. &client,
// We make sure algo or request are not rerun when they are not over. pub_api,
// in the case of quick refresh before new run of algo, make sure it doesn't happen multiple times. gui_api,
let last_request_expired = gui_api_xmrig,
last_request.lock().unwrap().elapsed() >= Duration::from_secs(60); gui_api_xp,
let should_refresh_before_next_algo = is_algo_started_once state_p2pool,
&& last_algorithm.lock().unwrap().elapsed() state_xmrig,
>= Duration::from_secs((XVB_TIME_ALGO as f32 * 0.95) as u64) state_xp,
&& last_request.lock().unwrap().elapsed() >= Duration::from_secs(25); xp_alive,
let process_alive = process.lock().unwrap().state == ProcessState::Alive; ) {
if ((last_request_expired || first_loop) info!("XvB Watchdog | Signal has stopped the loop");
|| (*retry.lock().unwrap() || is_algo_finished || should_refresh_before_next_algo) break;
&& process_alive) }
&& is_request_finished // let handle_algo_c = handle_algo.lock().unwrap();
{ let is_algo_started_once = handle_algo.lock().unwrap().is_some();
// do not wait for the request to finish so that they are retrieved at exactly one minute interval and not block the thread. let is_algo_finished = handle_algo
// Private API will also use this instant if XvB is Alive. .lock()
// first_loop is false here but could be changed to true under some conditions. .unwrap()
// will send a stop signal if public stats failed or update data with new one. .as_ref()
*handle_request.lock().unwrap() = Some(spawn( .is_some_and(|algo| algo.is_finished());
enc!((client, pub_api, gui_api, gui_api_p2pool, gui_api_xmrig, gui_api_xp, state_xvb, state_p2pool, state_xmrig, state_xp, process, last_algorithm, retry, handle_algo, time_donated, last_request) async move { let is_request_finished = handle_request
// needs to wait here for public stats to get private stats. .lock()
if last_request_expired || first_loop || should_refresh_before_next_algo { .unwrap()
XvbPubStats::update_stats(&client, &gui_api, &pub_api, &process).await; .as_ref()
*last_request.lock().unwrap() = Instant::now(); .is_some_and(|request: &JoinHandle<()>| request.is_finished())
} || handle_request.lock().unwrap().is_none();
// private stats needs valid token and address. // Send an HTTP API request only if one minute is passed since the last request or if first loop or if algorithm need to retry or if request is finished and algo is finished or almost finished (only public and private stats). We make sure public and private stats are refreshed before doing another run of the algo.
// other stats needs everything to be alive, so just require alive here for now. // We make sure algo or request are not rerun when they are not over.
// maybe later differentiate to add a way to get private stats without running the algo ? // in the case of quick refresh before new run of algo, make sure it doesn't happen multiple times.
if process.lock().unwrap().state == ProcessState::Alive { let last_request_expired =
// get current share to know if we are in a round and this is a required data for algo. last_request.lock().unwrap().elapsed() >= Duration::from_secs(60);
let share = gui_api_p2pool.lock().unwrap().sidechain_shares; let should_refresh_before_next_algo = is_algo_started_once
debug!("XvB | Number of current shares: {}", share); && last_algorithm.lock().unwrap().elapsed()
// private stats can be requested every minute or first loop or if the have almost finished. >= Duration::from_secs((XVB_TIME_ALGO as f32 * 0.95) as u64)
if last_request_expired || first_loop || should_refresh_before_next_algo { && last_request.lock().unwrap().elapsed() >= Duration::from_secs(25);
debug!("XvB Watchdog | Attempting HTTP private API request..."); let process_alive = process.lock().unwrap().state == ProcessState::Alive;
// reload private stats, it send a signal if error that will be captured on the upper thread. if ((last_request_expired || first_loop)
XvbPrivStats::update_stats( || (*retry.lock().unwrap()
&client, &state_p2pool.address, &state_xvb.token, &pub_api, &gui_api, &process, || is_algo_finished
) || should_refresh_before_next_algo)
.await; && process_alive)
*last_request.lock().unwrap() = Instant::now(); && is_request_finished
{
// verify in which round type we are // do not wait for the request to finish so that they are retrieved at exactly one minute interval and not block the thread.
let round = round_type(share, &pub_api); // Private API will also use this instant if XvB is Alive.
// refresh the round we participate in. // first_loop is false here but could be changed to true under some conditions.
debug!("XvB | Round type: {:#?}", round); // will send a stop signal if public stats failed or update data with new one.
pub_api.lock().unwrap().stats_priv.round_participate = round; *handle_request.lock().unwrap() = Some(spawn(
// verify if we are the winner of the current round enc!((client, pub_api, gui_api, gui_api_p2pool, gui_api_xmrig, gui_api_xp, state_xvb, state_p2pool, state_xmrig, state_xp, process, last_algorithm, retry, handle_algo, time_donated, last_request) async move {
if pub_api.lock().unwrap().stats_pub.winner // needs to wait here for public stats to get private stats.
== Helper::head_tail_of_monero_address(&state_p2pool.address).as_str() if last_request_expired || first_loop || should_refresh_before_next_algo {
{ XvbPubStats::update_stats(&client, &gui_api, &pub_api, &process).await;
pub_api.lock().unwrap().stats_priv.win_current = true *last_request.lock().unwrap() = Instant::now();
} }
} // private stats needs valid token and address.
let hashrate = current_controllable_hr(xp_alive, &gui_api_xp, &gui_api_xmrig); // other stats needs everything to be alive, so just require alive here for now.
let difficulty_data_is_ready = gui_api_p2pool.lock().unwrap().p2pool_difficulty_u64 > 100_000; // maybe later differentiate to add a way to get private stats without running the algo ?
if (first_loop || *retry.lock().unwrap()|| is_algo_finished) && hashrate > 0.0 && process.lock().unwrap().state == ProcessState::Alive && difficulty_data_is_ready if process.lock().unwrap().state == ProcessState::Alive {
{ // get current share to know if we are in a round and this is a required data for algo.
// if algo was started, it must not retry next loop. let share = gui_api_p2pool.lock().unwrap().sidechain_shares;
*retry.lock().unwrap() = false; debug!("XvB | Number of current shares: {}", share);
// reset instant because algo will start. // private stats can be requested every minute or first loop or if the have almost finished.
*last_algorithm.lock().unwrap() = Instant::now(); if last_request_expired || first_loop || should_refresh_before_next_algo {
*handle_algo.lock().unwrap() = Some(spawn(enc!((client, gui_api, gui_api_xmrig, gui_api_xp, state_xmrig, state_xp, time_donated, state_xvb) async move { debug!("XvB Watchdog | Attempting HTTP private API request...");
let token_xmrig = if xp_alive { // reload private stats, it send a signal if error that will be captured on the upper thread.
&state_xp.token XvbPrivStats::update_stats(
} else { &client, &state_p2pool.address, &state_xvb.token, &pub_api, &gui_api, &process,
&state_xmrig.token )
}; .await;
let rig = if xp_alive { *last_request.lock().unwrap() = Instant::now();
""
} else {
&state_xmrig.rig
};
algorithm(
&client,
&pub_api,
&gui_api,
&gui_api_xmrig,
&gui_api_xp,
&gui_api_p2pool,
token_xmrig,
&state_p2pool,
share,
&time_donated,
rig,
xp_alive,
state_xvb.p2pool_buffer
).await;
})));
} else {
// if xmrig is still at 0 HR but is alive and algorithm is skipped, recheck first 10s of xmrig inside algorithm next time (in one minute). Don't check if algo failed to start because state was not alive after getting private stats.
if (hashrate == 0.0 || !difficulty_data_is_ready) && process.lock().unwrap().state == ProcessState::Alive { // verify in which round type we are
*retry.lock().unwrap() = true let round = round_type(share, &pub_api);
// refresh the round we participate in.
debug!("XvB | Round type: {:#?}", round);
pub_api.lock().unwrap().stats_priv.round_participate = round;
// verify if we are the winner of the current round
if pub_api.lock().unwrap().stats_pub.winner
== Helper::head_tail_of_monero_address(&state_p2pool.address).as_str()
{
pub_api.lock().unwrap().stats_priv.win_current = true
} }
} }
let hashrate = current_controllable_hr(xp_alive, &gui_api_xp, &gui_api_xmrig);
let difficulty_data_is_ready = gui_api_p2pool.lock().unwrap().p2pool_difficulty_u64 > 100_000;
if (first_loop || *retry.lock().unwrap()|| is_algo_finished) && hashrate > 0.0 && process.lock().unwrap().state == ProcessState::Alive && difficulty_data_is_ready
{
// if algo was started, it must not retry next loop.
*retry.lock().unwrap() = false;
// reset instant because algo will start.
*last_algorithm.lock().unwrap() = Instant::now();
*handle_algo.lock().unwrap() = Some(spawn(enc!((client, gui_api, gui_api_xmrig, gui_api_xp, state_xmrig, state_xp, time_donated, state_xvb) async move {
let token_xmrig = if xp_alive {
&state_xp.token
} else {
&state_xmrig.token
};
let rig = if xp_alive {
""
} else {
&state_xmrig.rig
};
algorithm(
&client,
&pub_api,
&gui_api,
&gui_api_xmrig,
&gui_api_xp,
&gui_api_p2pool,
token_xmrig,
&state_p2pool,
share,
&time_donated,
rig,
xp_alive,
state_xvb.p2pool_buffer
).await;
})));
} else {
// if xmrig is still at 0 HR but is alive and algorithm is skipped, recheck first 10s of xmrig inside algorithm next time (in one minute). Don't check if algo failed to start because state was not alive after getting private stats.
} if (hashrate == 0.0 || !difficulty_data_is_ready) && process.lock().unwrap().state == ProcessState::Alive {
}), *retry.lock().unwrap() = true
)); }
}
}
}),
));
}
// if retry is false, next time the message about waiting for xmrig HR can be shown.
if !*retry.lock().unwrap() {
msg_retry_done = false;
}
// inform user that algorithm has not yet started because it is waiting for xmrig HR.
// show this message only once before the start of algo
if *retry.lock().unwrap() && !msg_retry_done {
let msg = if xp_alive {
"Algorithm is waiting for 1 minute average HR of XMRig-Proxy or p2pool data"
} else {
"Algorithm is waiting for 10 seconds average HR of XMRig or p2pool data"
};
output_console(&mut gui_api.lock().unwrap().output, msg, ProcessName::Xvb);
msg_retry_done = true;
}
// update indicator (time before switch and mining location) in private stats
// if algo not running, second message.
// will update countdown every second.
// verify current node which is set by algo or circonstances (failed node).
// verify given time set by algo and start time of current algo.
// will run only if XvB is alive.
// let algo time to start, so no countdown is shown.
update_indicator_algo(
is_algo_started_once,
is_algo_finished,
process,
pub_api,
*time_donated.lock().unwrap(),
&last_algorithm,
);
// first_loop is done, but maybe retry will allow the algorithm to retry again.
if first_loop {
first_loop = false;
}
// Sleep (only if 900ms hasn't passed)
} }
// if retry is false, next time the message about waiting for xmrig HR can be shown.
if !*retry.lock().unwrap() {
msg_retry_done = false;
}
// inform user that algorithm has not yet started because it is waiting for xmrig HR.
// show this message only once before the start of algo
if *retry.lock().unwrap() && !msg_retry_done {
let msg = if xp_alive {
"Algorithm is waiting for 1 minute average HR of XMRig-Proxy or p2pool data"
} else {
"Algorithm is waiting for 10 seconds average HR of XMRig or p2pool data"
};
output_console(&mut gui_api.lock().unwrap().output, msg, ProcessName::Xvb);
msg_retry_done = true;
}
// update indicator (time before switch and mining location) in private stats
// if algo not running, second message.
// will update countdown every second.
// verify current node which is set by algo or circonstances (failed node).
// verify given time set by algo and start time of current algo.
// will run only if XvB is alive.
// let algo time to start, so no countdown is shown.
update_indicator_algo(
is_algo_started_once,
is_algo_finished,
process,
pub_api,
*time_donated.lock().unwrap(),
&last_algorithm,
);
// first_loop is done, but maybe retry will allow the algorithm to retry again.
if first_loop {
first_loop = false;
}
// Sleep (only if 900ms hasn't passed)
sleep_end_loop(start_loop, ProcessName::Xvb).await; sleep_end_loop(start_loop, ProcessName::Xvb).await;
} }
} }