breadmill/breadsearch: narrow embedder lock scope, recover from poisoned mutexes, atomic index save + corrupt rebuild, fix GUI busy-poll, bump theme pin
- indexer.rs: embedder mutex is now locked only around each chunk's embed_document() call instead of the whole file's chunk loop, so a slow MIGraphX JIT compile on one file no longer blocks every query for minutes - sync_ext.rs (new)/indexer.rs/main.rs/serve.rs: added MutexExt::lock_recover(), replacing every .lock().unwrap() so a panic on one thread (poisoning the mutex) logs a warning and recovers instead of cascading into every future lock().unwrap() call - store.rs: save_index now writes to a .tmp sibling and renames atomically into place; Store::open recovers from an index that fails to load by wiping it and the SQLite files/chunks tables so the next scan rebuilds from scratch, instead of refusing to start at all. Note: this recovery only catches corruption usearch's loader reports as Err — verified experimentally that sufficiently garbled input segfaults the process instead, which no Rust-side handling can catch; the atomic save is what actually prevents the realistic mid-crash corruption case from arising - breadsearch/src/main.rs: GUI query-result wait switched from glib::idle_add_local (re-invoked every main-loop tick, pegging a core for the whole wait) to a 15ms glib::timeout_add_local poll - breadsearch/Cargo.toml: bread-theme pin bumped v0.2.8 -> v0.2.10 to match the rest of the family
This commit is contained in:
parent
3d83bd747e
commit
d01a3841d9
8 changed files with 164 additions and 30 deletions
41
breadmill/src/sync_ext.rs
Normal file
41
breadmill/src/sync_ext.rs
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
//! Poison-tolerant `Mutex` locking.
|
||||
//!
|
||||
//! A panic on any thread while holding `SharedState::store` or
|
||||
//! `SharedState::embedder` (e.g. an untrapped ONNX Runtime panic — only PDF
|
||||
//! extraction is `catch_unwind`-guarded, see `extract.rs`) poisons the
|
||||
//! `Mutex`. Every subsequent plain `.lock().unwrap()` — in the indexer *and*
|
||||
//! in every query handler in `serve.rs` — would then immediately panic too,
|
||||
//! silently bricking indexing and search until the daemon is restarted by
|
||||
//! hand.
|
||||
//!
|
||||
//! `Mutex` poisoning exists to flag "the data guarded by this lock might be
|
||||
//! in an inconsistent state," but every lock scope in this crate is a short,
|
||||
//! single-step SQLite call or usearch operation — nothing here spans a
|
||||
//! multi-step invariant across a single `lock()` call — so recovering the
|
||||
//! guard and logging loudly is a reasonable trade here: continuing to serve
|
||||
//! (and re-attempting the operation that panicked, on the next file/query)
|
||||
//! beats a daemon that silently stops answering everything after one panic.
|
||||
|
||||
use std::sync::{Mutex, MutexGuard};
|
||||
|
||||
pub trait MutexExt<T> {
|
||||
/// Like `.lock().unwrap()`, but recovers from a poisoned mutex instead
|
||||
/// of panicking again — logs once per recovery so it's visible in the
|
||||
/// daemon's own output, not just silently swallowed.
|
||||
fn lock_recover(&self) -> MutexGuard<'_, T>;
|
||||
}
|
||||
|
||||
impl<T> MutexExt<T> for Mutex<T> {
|
||||
fn lock_recover(&self) -> MutexGuard<'_, T> {
|
||||
match self.lock() {
|
||||
Ok(guard) => guard,
|
||||
Err(poisoned) => {
|
||||
eprintln!(
|
||||
"breadmill: WARNING: a mutex was poisoned by a panic on another thread; \
|
||||
recovering it and continuing instead of cascading the panic"
|
||||
);
|
||||
poisoned.into_inner()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue