diff --git a/bin/src/daemon.rs b/bin/src/daemon.rs index 9b6cce9..5435886 100644 --- a/bin/src/daemon.rs +++ b/bin/src/daemon.rs @@ -13,7 +13,6 @@ mod stats; use std::net::SocketAddr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; -use std::time::Duration; use crate::config::{parse_args, parse_config}; use crate::diag::run_diag_read_thread; @@ -41,7 +40,6 @@ use tokio::select; use tokio::sync::mpsc::{self, Sender}; use tokio::sync::{oneshot, RwLock}; use tokio::task::JoinHandle; -use tokio::time::sleep; use tokio_util::task::TaskTracker; type AppRouter = Router>; @@ -194,11 +192,6 @@ async fn main() -> Result<(), RayhunterError> { if !run_with_config(&args, &config).await? { return Ok(()); } - - // For some reason the diag device needs a very long time to become available again within - // the same process, on TP-Link M7350 v3. While process restart would reset it faster. - println!("Restarting Rayhunter. Waiting for 5 seconds..."); - sleep(Duration::from_secs(5)).await; } } diff --git a/lib/src/diag_device.rs b/lib/src/diag_device.rs index f364e1f..14aec89 100644 --- a/lib/src/diag_device.rs +++ b/lib/src/diag_device.rs @@ -10,9 +10,11 @@ use futures::TryStream; use log::{debug, error, info}; use std::io::ErrorKind; use std::os::fd::AsRawFd; +use std::time::Duration; use thiserror::Error; use tokio::fs::File; use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::time::sleep; pub type DiagResult = Result; @@ -85,6 +87,52 @@ pub struct DiagDevice { impl DiagDevice { pub async fn new() -> DiagResult { + Self::new_with_retries(Duration::from_secs(30)).await + } + + pub async fn new_with_retries(max_duration: Duration) -> DiagResult { + // For some reason the diag device needs a very long time to become available again with in + // the same process, on TP-Link M7350 v3. While process restart would reset it faster. + + let start_time = std::time::Instant::now(); + let max_delay = Duration::from_secs(5); + + let mut delay = Duration::from_millis(100); + let mut num_retries = 0; + + loop { + match Self::try_new().await { + Ok(device) => { + info!( + "Diag device initialization succeeded after {} retries", + num_retries + ); + return Ok(device); + } + Err(e) => { + num_retries += 1; + if start_time.elapsed() >= max_duration { + error!( + "Failed to initialize diag device after {:?}: {}", + max_duration, e + ); + return Err(e); + } + + info!( + "Diag device initialization failed {} times, retrying in {:?}: {}", + num_retries, delay, e + ); + sleep(delay).await; + + // Exponential backoff + delay = std::cmp::min(delay * 2, max_delay); + } + } + } + } + + async fn try_new() -> DiagResult { let diag_file = File::options() .read(true) .write(true)