fix(plugin): f64 pick coords + harden out-of-process lifecycle
Follow-ups after merging the out-of-process plugin isolation work: - Convert the out-of-process interactive adapter's `on_point` / `on_entity_pick` to `glam::DVec3`, matching the `CadCommand` trait, which moved to f64 pick coordinates. The branch predated that change and no longer compiled against the trait. - Contain plugin constructor panics in `export_plugin!`: the constructor runs across the C ABI boundary in `ocs_plugin_register`, where an unwinding panic is undefined behavior. Catch it and return null, which the loader already treats as a failed registration. - Reap killed runner processes. The spawn-timeout and disconnect paths called `child.kill()` without `wait()`, leaving a zombie on Unix until the host exited. Route both — and `shutdown()` — through a shared `reap()` that kills and waits on a detached thread. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
deec2a8fd3
commit
b4093724f2
4 changed files with 220 additions and 18 deletions
|
|
@ -97,9 +97,18 @@ macro_rules! export_plugin {
|
|||
#[no_mangle]
|
||||
pub extern "C" fn ocs_plugin_register(
|
||||
) -> *mut ::std::boxed::Box<dyn $crate::host::BuiltinPlugin> {
|
||||
let plugin: ::std::boxed::Box<dyn $crate::host::BuiltinPlugin> =
|
||||
::std::boxed::Box::new($ctor);
|
||||
::std::boxed::Box::into_raw(::std::boxed::Box::new(plugin))
|
||||
// The constructor runs across a C ABI boundary; a panic unwinding
|
||||
// past it is undefined behavior. Contain it and report failure as a
|
||||
// null pointer, which the host loader treats as "registration
|
||||
// failed" rather than crashing the runner process.
|
||||
match ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(|| {
|
||||
let plugin: ::std::boxed::Box<dyn $crate::host::BuiltinPlugin> =
|
||||
::std::boxed::Box::new($ctor);
|
||||
::std::boxed::Box::into_raw(::std::boxed::Box::new(plugin))
|
||||
})) {
|
||||
::std::result::Result::Ok(ptr) => ptr,
|
||||
::std::result::Result::Err(_) => ::std::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ impl PluginProcess {
|
|||
// Create the listener before spawning so the runner can connect immediately.
|
||||
let listener = ListenerOptions::new().name(socket_name_ref).create_sync()?;
|
||||
|
||||
let mut child = Command::new(&runner_path)
|
||||
let child = Command::new(&runner_path)
|
||||
.arg("--ocs-plugin-runner")
|
||||
.arg(&socket_name)
|
||||
.arg(cdylib_path)
|
||||
|
|
@ -99,11 +99,11 @@ impl PluginProcess {
|
|||
}
|
||||
Ok(Err(e)) => return Err(e.into()),
|
||||
Err(mpsc::RecvTimeoutError::Timeout) => {
|
||||
let _ = child.kill();
|
||||
reap(child);
|
||||
return Err(PluginError::SpawnTimeout(spawn_timeout()));
|
||||
}
|
||||
Err(mpsc::RecvTimeoutError::Disconnected) => {
|
||||
let _ = child.kill();
|
||||
reap(child);
|
||||
return Err(PluginError::RunnerExited);
|
||||
}
|
||||
};
|
||||
|
|
@ -244,11 +244,8 @@ impl PluginProcess {
|
|||
pub fn shutdown(&self) {
|
||||
let (stream, child) = self.take_resources();
|
||||
drop(stream);
|
||||
if let Some(mut child) = child {
|
||||
let _ = child.kill();
|
||||
std::thread::spawn(move || {
|
||||
let _ = child.wait();
|
||||
});
|
||||
if let Some(child) = child {
|
||||
reap(child);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -287,6 +284,16 @@ impl PluginProcess {
|
|||
}
|
||||
}
|
||||
|
||||
/// Kill a child process and reap it without blocking the caller. The blocking
|
||||
/// `wait()` runs in a detached thread so the host never stalls on a plugin, and
|
||||
/// the child is reaped rather than left as a zombie on Unix.
|
||||
fn reap(mut child: Child) {
|
||||
let _ = child.kill();
|
||||
std::thread::spawn(move || {
|
||||
let _ = child.wait();
|
||||
});
|
||||
}
|
||||
|
||||
fn shutdown_error() -> PluginError {
|
||||
PluginError::Io(std::io::Error::new(
|
||||
std::io::ErrorKind::NotConnected,
|
||||
|
|
|
|||
Loading…
Reference in a new issue