diff --git a/crates/oakplugin/src/host.rs b/crates/oakplugin/src/host.rs index 3254d53de..89bc30c11 100644 --- a/crates/oakplugin/src/host.rs +++ b/crates/oakplugin/src/host.rs @@ -605,6 +605,11 @@ pub struct Plugin { /// OfxPlugin 结构指针(kOfxImageEffectPropPluginHandle 属性用; /// 公开:测试构造假插件需要)。 pub ofx_plugin: *mut c_void, + /// 二进制已卸载(host shutdown / unload_all)。置位后任何 + /// call_action/call_entry 直接失败而不触碰入口——插件入口指向 + /// 已 dlclose 的代码,调用即 SIGSEGV(测试串行 shutdown/重扫描 + /// 会产生跨代实例,它们持有的正是旧代插件记录)。 + pub unloaded: std::sync::atomic::AtomicBool, } // dlopen 句柄与 OfxPlugin 指针是**不透明令牌**(只经 dlsym/插件 @@ -654,6 +659,9 @@ impl Plugin { in_args: &PropertySet, out_args: &PropertySet, ) -> i32 { + if self.unloaded.load(std::sync::atomic::Ordering::Acquire) { + return status::ERR_FATAL; + } // 属性集经裸指针(标签 0)传给插件(property suite 接受)。 let in_ptr = in_args as *const PropertySet as *mut c_void; let out_ptr = out_args as *const PropertySet as *mut c_void; @@ -676,6 +684,9 @@ impl Plugin { in_args: &PropertySet, out_args: &PropertySet, ) -> i32 { + if self.unloaded.load(std::sync::atomic::Ordering::Acquire) { + return status::ERR_FATAL; + } let in_ptr = in_args as *const PropertySet as *mut c_void; let out_ptr = out_args as *const PropertySet as *mut c_void; let action = cs(action); @@ -991,6 +1002,7 @@ impl PluginCache { lib, entry, ofx_plugin: ofx as *mut c_void, + unloaded: std::sync::atomic::AtomicBool::new(false), }; // 根描述符属性(HS: effectDescriptorStuff,ofxhImageEffect.cpp:133-155)。 @@ -1070,7 +1082,16 @@ impl PluginCache { /// 卸载全部(shutdown 路径)。`scanned_paths` 一并清空—— /// 否则再 init 后的扫描会被去重短路(缓存已空但路径仍在)。 + /// 卸载前先给每个插件置 `unloaded`:实例可能跨代存活(测试的 + /// 串行 shutdown/重扫描),它们持有的旧代入口已随 dlclose 失效, + /// 置位后 call_action 直接失败而非跳野指针。 pub(crate) fn unload_all(&self) { + { + let plugins = self.plugins.lock().unwrap_or_else(|e| e.into_inner()); + for p in plugins.iter() { + p.unloaded.store(true, std::sync::atomic::Ordering::Release); + } + } self.plugins .lock() .unwrap_or_else(|e| e.into_inner()) diff --git a/crates/oakplugin/src/node_factory.rs b/crates/oakplugin/src/node_factory.rs index 2bc485281..4b9cffc52 100644 --- a/crates/oakplugin/src/node_factory.rs +++ b/crates/oakplugin/src/node_factory.rs @@ -1131,6 +1131,7 @@ mod tests { lib: std::ptr::null_mut(), entry: dummy_entry, ofx_plugin: std::ptr::null_mut(), + unloaded: std::sync::atomic::AtomicBool::new(false), }); let mut params = ParamSetInstance { params: Vec::new() }; params.params.push(Box::new(ParamInstance::from_def(ParamDef::new( @@ -1235,6 +1236,7 @@ mod tests { lib: std::ptr::null_mut(), entry: dummy_entry, ofx_plugin: std::ptr::null_mut(), + unloaded: std::sync::atomic::AtomicBool::new(false), }); let inst = crate::instance::Instance { props: crate::property::PropertySet::new(), diff --git a/crates/oakplugin/src/suites/image_effect.rs b/crates/oakplugin/src/suites/image_effect.rs index d146b0fe4..6f3ba88d8 100644 --- a/crates/oakplugin/src/suites/image_effect.rs +++ b/crates/oakplugin/src/suites/image_effect.rs @@ -574,6 +574,7 @@ mod tests { lib: std::ptr::null_mut(), entry: dummy_entry, ofx_plugin: std::ptr::null_mut(), + unloaded: std::sync::atomic::AtomicBool::new(false), }), context: "OfxImageEffectContextFilter".into(), params: crate::param::ParamSetInstance { params: vec![] }, diff --git a/crates/oakplugin/src/suites/interact.rs b/crates/oakplugin/src/suites/interact.rs index 0867e4e67..fe11ed180 100644 --- a/crates/oakplugin/src/suites/interact.rs +++ b/crates/oakplugin/src/suites/interact.rs @@ -575,6 +575,7 @@ mod tests { lib: std::ptr::null_mut(), entry: dummy_entry, ofx_plugin: std::ptr::null_mut(), + unloaded: std::sync::atomic::AtomicBool::new(false), }; let interact = Interact::new(Arc::new(plugin), dummy_entry, std::ptr::null_mut()); let handle = interact.handle(); @@ -648,6 +649,7 @@ mod tests { lib: std::ptr::null_mut(), entry: recording_entry, ofx_plugin: std::ptr::null_mut(), + unloaded: std::sync::atomic::AtomicBool::new(false), }; let interact = Interact::new(Arc::new(plugin), recording_entry, std::ptr::null_mut()); @@ -717,6 +719,7 @@ mod tests { lib: std::ptr::null_mut(), entry: capture_entry, ofx_plugin: std::ptr::null_mut(), + unloaded: std::sync::atomic::AtomicBool::new(false), }; let interact = Interact::new(Arc::new(plugin), capture_entry, std::ptr::null_mut()); // 设 pixelScale=2 → canonical = viewport/2。 @@ -768,6 +771,7 @@ mod tests { lib: std::ptr::null_mut(), entry: capture_entry, ofx_plugin: std::ptr::null_mut(), + unloaded: std::sync::atomic::AtomicBool::new(false), }; let interact = Interact::new(Arc::new(plugin), capture_entry, std::ptr::null_mut()); assert_eq!(interact.key_down(crate::host::KEY_RETURN, "", 1.0), status::OK); @@ -805,6 +809,7 @@ mod tests { lib: std::ptr::null_mut(), entry: counting_entry, ofx_plugin: std::ptr::null_mut(), + unloaded: std::sync::atomic::AtomicBool::new(false), }; let interact = Interact::new(Arc::new(plugin), counting_entry, std::ptr::null_mut()); interact.destroy(); diff --git a/crates/oakplugin/src/suites/param.rs b/crates/oakplugin/src/suites/param.rs index 5ae376485..9f2bc524e 100644 --- a/crates/oakplugin/src/suites/param.rs +++ b/crates/oakplugin/src/suites/param.rs @@ -772,6 +772,7 @@ mod tests { lib: std::ptr::null_mut(), entry: dummy_entry, ofx_plugin: std::ptr::null_mut(), + unloaded: std::sync::atomic::AtomicBool::new(false), }) } diff --git a/crates/oakplugin/src/suites/parametric.rs b/crates/oakplugin/src/suites/parametric.rs index 115dbc369..0e6efbbf7 100644 --- a/crates/oakplugin/src/suites/parametric.rs +++ b/crates/oakplugin/src/suites/parametric.rs @@ -449,6 +449,7 @@ mod tests { lib: std::ptr::null_mut(), entry: dummy_entry, ofx_plugin: std::ptr::null_mut(), + unloaded: std::sync::atomic::AtomicBool::new(false), }) } diff --git a/crates/oakplugin/tests/suites_test.rs b/crates/oakplugin/tests/suites_test.rs index 26b95990b..c192fa81d 100644 --- a/crates/oakplugin/tests/suites_test.rs +++ b/crates/oakplugin/tests/suites_test.rs @@ -77,6 +77,7 @@ fn dummy_plugin(descriptor: EffectDescriptor) -> std::sync::Arc