Skip to content

Commit

Permalink
handle module thread errors
Browse files Browse the repository at this point in the history
  • Loading branch information
doums committed Aug 21, 2024
1 parent 7c45463 commit 4205b95
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "baru"
version = "0.4.0"
version = "0.4.1"
description = "A simple system monitor for WM statusbar"
authors = ["pierre <[email protected]>"]
edition = "2021"
Expand Down
5 changes: 5 additions & 0 deletions baru.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ tick: 100
#
pulse_tick: 100

# failed_icon: String, default: ✗
#
# The icon printed when a module has failed.
#
failed_icon: ''

# Each module take a "format" option.
# This option is a string and the following markups will be replaced respectively by the value and the label of the module:
Expand Down
13 changes: 8 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

pub mod cli;
mod error;
mod http;
mod module;
mod modules;
mod netlink;
mod pulse;
pub mod trace;
pub mod util;
mod http;

use anyhow::{anyhow, Result};
use error::Error;
Expand Down Expand Up @@ -43,6 +43,7 @@ pub struct ModuleMsg(char, Option<String>, Option<String>);
pub struct Config {
format: String,
pub tick: Option<u32>,
failed_icon: Option<String>,
pulse_tick: Option<u32>,
battery: Option<BatteryConfig>,
brightness: Option<BrightnessConfig>,
Expand Down Expand Up @@ -87,25 +88,26 @@ impl<'a> Baru<'a> {
}

#[instrument(skip_all)]
pub fn start(&self) -> Result<()> {
pub fn start(&mut self) -> Result<()> {
// check if any module needs pulse, i.e. sound or mic modules
let need_pulse = self.modules.iter().any(|m| m.key == 's' || m.key == 'i');
if need_pulse {
pulse::init(self.config);
}
for data in &self.modules {
for data in &mut self.modules {
let builder = thread::Builder::new().name(format!("mod_{}", data.module.name()));
let cloned_m_conf = self.config.clone();
let tx1 = mpsc::Sender::clone(&self.channel.0);
let run = data.module.run_fn();
let key = data.key;
let c_name = data.module.name().to_string();
builder.spawn(move || -> Result<(), Error> {
info!("[{}] module starting", c_name);
let handle = builder.spawn(move || -> Result<(), Error> {
run(key, cloned_m_conf, tx1)
.inspect_err(|e| error!("[{}] module failed: {}", c_name, e))?;
Ok(())
})?;
data.start(handle);
info!("[{}] module started", data.module.name());
}
Ok(())
}
Expand All @@ -124,6 +126,7 @@ impl<'a> Baru<'a> {
pub fn update(&mut self) -> Result<()> {
let messages: Vec<ModuleMsg> = self.channel.1.try_iter().collect();
for module in &mut self.modules {
module.update_state().ok();
let mut iter = messages.iter().rev();
let message = iter.find(|v| v.0 == module.key);
if let Some(value) = message {
Expand Down
71 changes: 70 additions & 1 deletion src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ use crate::modules::wired::Wired;
use crate::modules::wireless::Wireless;
use crate::Config;
use crate::ModuleMsg;

use anyhow::{anyhow, Result};
use std::convert::TryFrom;
use std::sync::mpsc::Sender;
use std::thread::JoinHandle;
use tracing::{error, info, instrument};

const MODULE_FAILED_ICON: &str = "✗";

pub type RunPtr = fn(char, Config, Sender<ModuleMsg>) -> Result<(), Error>;

Expand Down Expand Up @@ -137,19 +143,39 @@ impl<'a> Bar for Module<'a> {
}
}

#[derive(Debug, Clone)]
pub enum ModuleState {
NotStarted,
Running,
/// Module's `run` function returned without errors
Finished,
/// Module's `run` function returned an error or panicked
Failed,
}

#[derive(Debug)]
pub struct ModuleData<'a> {
pub key: char,
pub module: Module<'a>,
data: Option<String>,
state: ModuleState,
handle: Option<JoinHandle<Result<(), Error>>>,
failed_placeholder: String,
}

impl<'a> ModuleData<'a> {
pub fn new(key: char, config: &'a Config) -> Result<Self, Error> {
pub fn new(key: char, config: &'a Config) -> Result<Self> {
Ok(ModuleData {
key,
module: Module::try_from((key, config))?,
data: None,
state: ModuleState::NotStarted,
handle: None,
failed_placeholder: config
.failed_icon
.as_ref()
.map(|icon| format!("{}:{}", &key, icon))
.unwrap_or_else(|| format!("{}:{}", &key, MODULE_FAILED_ICON)),
})
}

Expand All @@ -167,10 +193,53 @@ impl<'a> ModuleData<'a> {
}

pub fn output(&self) -> &str {
if matches!(self.state, ModuleState::Failed) {
return &self.failed_placeholder;
}

if let Some(data) = &self.data {
data
} else {
self.module.placeholder()
}
}

pub fn start(&mut self, handle: JoinHandle<Result<(), Error>>) {
self.handle = Some(handle);
self.state = ModuleState::Running;
}

#[instrument(skip_all)]
pub fn update_state(&mut self) -> Result<()> {
let Some(handle) = &self.handle else {
return Ok(());
};
if !handle.is_finished() {
return Ok(());
}

// module thread has finished for some reason, join it
// and update the state accordingly
self.state = match self
.handle
.take()
.ok_or(anyhow!("failed to unwrap handle"))?
.join()
{
Ok(Ok(_)) => {
info!("[{}] module finished", self.module.name());
ModuleState::Finished
}
Ok(Err(e)) => {
error!("[{}] module failed: {}", self.module.name(), e);
ModuleState::Failed
}
Err(_) => {
let msg = format!("[{}] module panicked", self.module.name());
error!("{}", &msg);
ModuleState::Failed
}
};
Ok(())
}
}

0 comments on commit 4205b95

Please sign in to comment.