perf(meeting): cache sysinfo System for the meeting-detection poller
`detect_meeting_processes` is called every 15 s when meeting-auto-capture is enabled. The previous `list_running_process_names` allocated a fresh `sysinfo::System` per call and walked /proc cold; on a busy host (~300 processes) that's ~50–100 ms of work, every poll, forever. Add `kon_core::process_watch::ProcessLister`, a thin wrapper around a long-lived `System` whose process table is refreshed in place. The Tauri host holds one behind a `Mutex<ProcessLister>` in a new `MeetingState` managed at app setup. The free `list_running_process_names` is kept as a convenience that constructs a fresh `ProcessLister` per call — its only remaining caller is the existing smoke test. - ProcessLister + Default in crates/core/src/process_watch.rs. - MeetingState in src-tauri/src/commands/meeting.rs; the command takes it via `tauri::State` and locks for the duration of the snapshot. - src-tauri/src/lib.rs registers MeetingState alongside the other managed states. https://claude.ai/code/session_0189xUb6ie6t9qHkzatGZ9Rb
This commit is contained in:
@@ -5,13 +5,46 @@
|
||||
//! that reminds the user to start recording with their hotkey. We do not
|
||||
//! start recording from this signal — the user decides.
|
||||
|
||||
use kon_core::process_watch;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use kon_core::process_watch::{self, ProcessLister};
|
||||
|
||||
/// Tauri-managed state for the meeting poller. Holds a long-lived
|
||||
/// `ProcessLister` so each poll refreshes the existing `sysinfo::System`
|
||||
/// in place instead of allocating a fresh one — the previous implementation
|
||||
/// rebuilt the process table from scratch every 15 s.
|
||||
pub struct MeetingState {
|
||||
lister: Mutex<ProcessLister>,
|
||||
}
|
||||
|
||||
impl MeetingState {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
lister: Mutex::new(ProcessLister::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MeetingState {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn detect_meeting_processes(patterns: Vec<String>) -> Result<Vec<String>, String> {
|
||||
pub fn detect_meeting_processes(
|
||||
state: tauri::State<'_, MeetingState>,
|
||||
patterns: Vec<String>,
|
||||
) -> Result<Vec<String>, String> {
|
||||
if patterns.is_empty() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
let processes = process_watch::list_running_process_names();
|
||||
let processes = {
|
||||
let mut lister = state
|
||||
.lister
|
||||
.lock()
|
||||
.map_err(|e| format!("MeetingState lister poisoned: {e}"))?;
|
||||
lister.snapshot()
|
||||
};
|
||||
Ok(process_watch::match_meeting_patterns(&processes, &patterns))
|
||||
}
|
||||
|
||||
@@ -250,6 +250,7 @@ pub fn run() {
|
||||
app.manage(commands::audio::NativeCaptureState::new());
|
||||
app.manage(commands::live::LiveTranscriptionState::default());
|
||||
app.manage(commands::tts::TtsState::new());
|
||||
app.manage(commands::meeting::MeetingState::new());
|
||||
|
||||
app.manage(AppState {
|
||||
whisper_engine: Arc::new(LocalEngine::new(EngineName::new("whisper"))),
|
||||
|
||||
Reference in New Issue
Block a user