Skip to content

Commit b52a71d

Browse files
Fix external STT server lifecycle to ensure single instance and proper cleanup (#2050)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
1 parent baf3bfd commit b52a71d

File tree

4 files changed

+61
-0
lines changed

4 files changed

+61
-0
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/host/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ edition = "2021"
77
mac_address2 = "2.0.2"
88
machine-uid = "0.5.4"
99
sysinfo = { workspace = true }
10+
tokio = { workspace = true, features = ["time"] }

crates/host/src/lib.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,46 @@ pub enum ProcessMatcher {
2222
Sidecar,
2323
}
2424

25+
pub fn has_processes_matching(matcher: &ProcessMatcher) -> bool {
26+
let target = match matcher {
27+
ProcessMatcher::Name(name) => name.clone(),
28+
ProcessMatcher::Sidecar => "stt".to_string(),
29+
};
30+
31+
let mut sys = sysinfo::System::new();
32+
sys.refresh_processes(sysinfo::ProcessesToUpdate::All, true);
33+
34+
for (_, process) in sys.processes() {
35+
let process_name = process.name().to_string_lossy();
36+
if process_name.contains(&target) {
37+
return true;
38+
}
39+
}
40+
41+
false
42+
}
43+
44+
pub async fn wait_for_processes_to_terminate(
45+
matcher: ProcessMatcher,
46+
max_wait_ms: u64,
47+
check_interval_ms: u64,
48+
) -> bool {
49+
if check_interval_ms == 0 {
50+
return false;
51+
}
52+
53+
let max_iterations = max_wait_ms / check_interval_ms;
54+
55+
for _ in 0..max_iterations {
56+
if !has_processes_matching(&matcher) {
57+
return true;
58+
}
59+
tokio::time::sleep(std::time::Duration::from_millis(check_interval_ms)).await;
60+
}
61+
62+
!has_processes_matching(&matcher)
63+
}
64+
2565
pub fn kill_processes_by_matcher(matcher: ProcessMatcher) -> u16 {
2666
let target = match matcher {
2767
ProcessMatcher::Name(name) => name,

plugins/local-stt/src/server/supervisor.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ pub async fn stop_stt_server(
125125
ServerType::External => wait_for_actor_shutdown(ExternalSTTActor::name()).await,
126126
}
127127

128+
if matches!(server_type, ServerType::External) {
129+
wait_for_process_cleanup().await;
130+
}
131+
128132
Ok(())
129133
}
130134

@@ -144,3 +148,18 @@ async fn wait_for_actor_shutdown(actor_name: ractor::ActorName) {
144148
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
145149
}
146150
}
151+
152+
async fn wait_for_process_cleanup() {
153+
let process_terminated =
154+
hypr_host::wait_for_processes_to_terminate(hypr_host::ProcessMatcher::Sidecar, 5000, 100)
155+
.await;
156+
157+
if !process_terminated {
158+
tracing::warn!("external_stt_process_did_not_terminate_in_time");
159+
let killed = hypr_host::kill_processes_by_matcher(hypr_host::ProcessMatcher::Sidecar);
160+
if killed > 0 {
161+
tracing::info!("force_killed_stt_processes: {}", killed);
162+
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
163+
}
164+
}
165+
}

0 commit comments

Comments
 (0)