Fix 18 issues flagged in audit + bump to v0.6.2
P1-A: normalizer derives `online` from rtnetlink event kind (link.up/down,
route.default.changed, address.added/removed) so bread.network.connected
fires correctly on all systems using rtnetlink.
P1-B: stream_events consumes the subscribe ack before the event loop so the
first line is not printed as garbage.
P1-C: UPowerAdapter::probe() validates D-Bus synchronously before committing;
the sysfs fallback now actually triggers when D-Bus is unavailable.
P2-A: profile.list returns the full profile state (active + history) instead
of the always-empty profiles map.
P2-B: profile history capped at 50 entries in both StateCommand and
apply_event_to_state to prevent unbounded growth.
P2-C: RtnetlinkAdapter::new() no longer spawns an orphaned tokio task;
it validates availability by constructing and immediately dropping the
connection tuple.
P2-D: Lua-side hyprland_request_socket() logs a warn when multiple
Hyprland instances are found, matching the adapter-side behaviour.
P2-E: Malformed JSON from an IPC client returns an error response and
continues rather than closing the entire connection.
P3-A: Remove the `ends_with(".*")` prefix-match shortcut from both the
subscription table and the IPC event filter. `bread.*` now means
one segment (matching documented API semantics: `* = one segment`).
Tests updated accordingly.
P4-A: Remove unused `git2` and `glob` workspace dependencies (left over
from bread-sync extraction).
P4-B: breadd dev-dependency `tempfile` declared via workspace = true.
P4-C: Remove unreachable XDG_CONFIG_HOME branch in modules_dir(); dirs
already reads that var internally before returning None.
P4-D: Delete duplicate send_request_with_stream(); print_doctor() now
uses socket.exists() + send_request() directly.
P5-A: release.yml drops `--lib` from cargo test so integration tests run
in the release gate.
P6-A: bluetooth_spawn / bluetooth_query replace expect() on tokio runtime
construction with error logging / error propagation.
P6-B: Spin loops in lua/mod.rs add std:🧵:yield_now() after the
PAUSE hint to reduce CPU burn under sustained RwLock contention.
P6-C: All Mutex::lock().expect("... poisoned") in lua/mod.rs replaced with
unwrap_or_else(|e| e.into_inner()) for poison recovery.
P7-B: bread.system.startup event moved from main.rs into ipc::Server::serve()
so it fires after the socket is bound (smaller race window for early
subscribers).
This commit is contained in:
parent
0f3136ca8d
commit
3115a4230b
17 changed files with 146 additions and 136 deletions
|
|
@ -254,23 +254,23 @@ impl LuaEngine {
|
|||
self.lua = Lua::new();
|
||||
self.handlers
|
||||
.lock()
|
||||
.expect("lua handlers mutex poisoned")
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.clear();
|
||||
self.watch_ids
|
||||
.lock()
|
||||
.expect("lua watch ids mutex poisoned")
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.clear();
|
||||
self.modules
|
||||
.lock()
|
||||
.expect("lua modules mutex poisoned")
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.clear();
|
||||
self.module_decls
|
||||
.lock()
|
||||
.expect("lua module decls mutex poisoned")
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.clear();
|
||||
self.module_order
|
||||
.lock()
|
||||
.expect("lua module order mutex poisoned")
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.clear();
|
||||
|
||||
self.install_api()?;
|
||||
|
|
@ -1138,7 +1138,7 @@ impl LuaEngine {
|
|||
let mut decl_map = self
|
||||
.module_decls
|
||||
.lock()
|
||||
.expect("module decls mutex poisoned");
|
||||
.unwrap_or_else(|e| e.into_inner());
|
||||
decl_map.clear();
|
||||
for decl in &ordered {
|
||||
decl_map.insert(decl.name.clone(), decl.clone());
|
||||
|
|
@ -1176,7 +1176,7 @@ impl LuaEngine {
|
|||
*self
|
||||
.module_order
|
||||
.lock()
|
||||
.expect("module order mutex poisoned") = load_order;
|
||||
.unwrap_or_else(|e| e.into_inner()) = load_order;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1229,7 +1229,7 @@ impl LuaEngine {
|
|||
|
||||
fn handle_event(&self, id: SubscriptionId, event: BreadEvent) -> Result<()> {
|
||||
let (callback, filter, raw_kind, kind, module) = {
|
||||
let handlers = self.handlers.lock().expect("lua handlers mutex poisoned");
|
||||
let handlers = self.handlers.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let Some(entry) = handlers.get(&id) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
|
@ -1290,7 +1290,7 @@ impl LuaEngine {
|
|||
|
||||
fn handle_timer(&self, id: TimerId) -> Result<()> {
|
||||
let (callback, repeating) = {
|
||||
let timers = self.timers.lock().expect("lua timers mutex poisoned");
|
||||
let timers = self.timers.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let Some(entry) = timers.get(&id) else {
|
||||
return Ok(());
|
||||
};
|
||||
|
|
@ -1334,7 +1334,7 @@ impl LuaEngine {
|
|||
let order = self
|
||||
.module_order
|
||||
.lock()
|
||||
.expect("module order mutex poisoned")
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.clone();
|
||||
for name in order {
|
||||
if let Some(hook) = self.get_module_hook(&name, "on_reload") {
|
||||
|
|
@ -1356,7 +1356,7 @@ impl LuaEngine {
|
|||
let order = self
|
||||
.module_order
|
||||
.lock()
|
||||
.expect("module order mutex poisoned")
|
||||
.unwrap_or_else(|e| e.into_inner())
|
||||
.clone();
|
||||
for name in order.into_iter().rev() {
|
||||
if let Some(hook) = self.get_module_hook(&name, "on_unload") {
|
||||
|
|
@ -1792,6 +1792,7 @@ fn state_value_to_lua<'lua>(
|
|||
break g;
|
||||
}
|
||||
std::hint::spin_loop();
|
||||
std::thread::yield_now();
|
||||
};
|
||||
let mut value =
|
||||
serde_json::to_value(&*snapshot).map_err(|e| LuaError::external(e.to_string()))?;
|
||||
|
|
@ -1820,6 +1821,7 @@ fn module_store_get(
|
|||
break g;
|
||||
}
|
||||
std::hint::spin_loop();
|
||||
std::thread::yield_now();
|
||||
};
|
||||
let entry = guard.modules.iter().find(|m| m.name == module)?;
|
||||
entry.store.get(key).cloned()
|
||||
|
|
@ -1836,6 +1838,7 @@ fn module_store_set(
|
|||
break g;
|
||||
}
|
||||
std::hint::spin_loop();
|
||||
std::thread::yield_now();
|
||||
};
|
||||
if let Some(entry) = guard.modules.iter_mut().find(|m| m.name == module) {
|
||||
entry.store.insert(key, value);
|
||||
|
|
@ -2210,7 +2213,13 @@ fn hyprland_request_socket() -> Result<PathBuf> {
|
|||
hypr_dir.display()
|
||||
)),
|
||||
1 => Ok(sockets.remove(0)),
|
||||
_ => Ok(sockets.remove(0)),
|
||||
_ => {
|
||||
warn!(
|
||||
"multiple Hyprland instances found in {}; using the first one",
|
||||
hypr_dir.display()
|
||||
);
|
||||
Ok(sockets.remove(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -2274,11 +2283,17 @@ where
|
|||
Fut: std::future::Future<Output = ()>,
|
||||
{
|
||||
std::thread::spawn(move || {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
let rt = match tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("bluetooth action thread")
|
||||
.block_on(factory());
|
||||
{
|
||||
Ok(rt) => rt,
|
||||
Err(e) => {
|
||||
tracing::error!(error = %e, "bluetooth action: failed to build tokio runtime");
|
||||
return;
|
||||
}
|
||||
};
|
||||
rt.block_on(factory());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2292,11 +2307,13 @@ where
|
|||
{
|
||||
let (tx, rx) = std::sync::mpsc::sync_channel(1);
|
||||
std::thread::spawn(move || {
|
||||
let result = tokio::runtime::Builder::new_current_thread()
|
||||
let result = match tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("bluetooth query thread")
|
||||
.block_on(factory());
|
||||
{
|
||||
Ok(rt) => rt.block_on(factory()),
|
||||
Err(e) => Err(anyhow::anyhow!("bluetooth query: failed to build tokio runtime: {e}")),
|
||||
};
|
||||
let _ = tx.send(result);
|
||||
});
|
||||
rx.recv()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue