forked from 1b-agent/opencadstudio-upstream
Fix 2: optimize src
This commit is contained in:
1 parent
d430981da3
commit
8eb199bfc7
5 files changed
+360
-157
No files matched your search
+36
-62
@@ -221,7 +221,7 @@ fn parse_string_array(s: &str) -> Vec<String> {
|
||||
// ── Runtime loading (desktop only) ──────────────────────────────────────────
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub(crate) use loader::with_loaded;
|
||||
pub(crate) use loader::with_manager;
|
||||
|
||||
#[cfg(all(not(target_arch = "wasm32"), not(test)))]
|
||||
pub(crate) use loader::{load_at_startup, loaded_ids};
|
||||
@@ -229,61 +229,60 @@ pub(crate) use loader::{load_at_startup, loaded_ids};
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[cfg_attr(test, allow(dead_code))]
|
||||
mod loader {
|
||||
use super::{lib_extension, ExternalPlugin};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// A loaded external plugin. Holds the spawned process and a shareable
|
||||
/// ribbon module built from the process's cached ribbon data.
|
||||
pub struct LoadedPlugin {
|
||||
pub process: Arc<ocs_plugin_api::process::PluginProcess>,
|
||||
pub module: ocs_plugin_api::ribbon::owned::SharedCadModule,
|
||||
pub id: String,
|
||||
}
|
||||
|
||||
use super::lib_extension;
|
||||
use ocs_plugin_api::process::PluginManager;
|
||||
use std::cell::RefCell;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
// Process-wide store of spawned external plugins. The runner processes stay
|
||||
// alive for the whole session; this is filled once at startup.
|
||||
// Process-wide plugin manager. Drop kills every runner process asynchronously
|
||||
// so host shutdown is never delayed by a plugin.
|
||||
thread_local! {
|
||||
static LOADED: RefCell<Vec<LoadedPlugin>> = const { RefCell::new(Vec::new()) };
|
||||
static MANAGER: RefCell<Option<PluginManager>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
/// Discover packages and spawn every API-compatible one as a separate
|
||||
/// process. Call once at startup. Returns per-id results so the host can
|
||||
/// report load failures.
|
||||
pub(crate) fn load_at_startup(app: &mut crate::app::OpenCADStudio) -> Vec<(String, Result<(), String>)> {
|
||||
pub(crate) fn load_at_startup(
|
||||
app: &mut crate::app::OpenCADStudio,
|
||||
) -> Vec<(String, Result<(), String>)> {
|
||||
let discovered = super::discover();
|
||||
let mut manager = PluginManager::new();
|
||||
let mut out = Vec::new();
|
||||
LOADED.with(|cell| {
|
||||
let mut store = cell.borrow_mut();
|
||||
if !store.is_empty() {
|
||||
return; // already loaded this session
|
||||
for d in &discovered {
|
||||
if !d.api_compatible() || !d.lib_present {
|
||||
continue;
|
||||
}
|
||||
for d in &discovered {
|
||||
if !d.api_compatible() || !d.lib_present {
|
||||
continue;
|
||||
}
|
||||
match load(d, app) {
|
||||
Ok(lp) => {
|
||||
out.push((lp.id.clone(), Ok(())));
|
||||
store.push(lp);
|
||||
}
|
||||
Err(e) => out.push((d.id.clone(), Err(e))),
|
||||
}
|
||||
let Some(path) = lib_file(&d.dir) else {
|
||||
out.push((d.id.clone(), Err("no native library in package".to_string())));
|
||||
continue;
|
||||
};
|
||||
let mut host = crate::app::plugin_host::HostSession::new(app, 0);
|
||||
match manager.load(&path, &mut host) {
|
||||
Ok(id) => out.push((id, Ok(()))),
|
||||
Err(e) => out.push((d.id.clone(), Err(e.to_string()))),
|
||||
}
|
||||
});
|
||||
}
|
||||
MANAGER.with(|m| *m.borrow_mut() = Some(manager));
|
||||
out
|
||||
}
|
||||
|
||||
/// Ids of the plugins currently loaded in the process store.
|
||||
pub fn loaded_ids() -> Vec<String> {
|
||||
LOADED.with(|c| c.borrow().iter().map(|lp| lp.id.clone()).collect())
|
||||
MANAGER.with(|m| m.borrow().as_ref().map(|mgr| mgr.ids()).unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Run `f` over the loaded plugins (borrowing the store).
|
||||
pub fn with_loaded<R>(f: impl FnOnce(&[LoadedPlugin]) -> R) -> R {
|
||||
LOADED.with(|c| f(&c.borrow()))
|
||||
/// Run `f` with a reference to the loaded plugin manager.
|
||||
pub fn with_manager<R>(f: impl FnOnce(&PluginManager) -> R) -> R {
|
||||
MANAGER.with(|m| {
|
||||
let guard = m.borrow();
|
||||
if let Some(manager) = guard.as_ref() {
|
||||
return f(manager);
|
||||
}
|
||||
drop(guard);
|
||||
let empty = PluginManager::new();
|
||||
f(&empty)
|
||||
})
|
||||
}
|
||||
|
||||
/// Path to the native library beside `plugin.toml`, if any.
|
||||
@@ -294,31 +293,6 @@ mod loader {
|
||||
(p.extension().and_then(|s| s.to_str()) == Some(ext)).then_some(p)
|
||||
})
|
||||
}
|
||||
|
||||
/// Spawn a discovered package's `cdylib` in a separate process and cache
|
||||
/// its ribbon module. The runner performs the API version gate before any
|
||||
/// plugin code runs.
|
||||
pub fn load(
|
||||
p: &ExternalPlugin,
|
||||
app: &mut crate::app::OpenCADStudio,
|
||||
) -> Result<LoadedPlugin, String> {
|
||||
let path = lib_file(&p.dir).ok_or("no native library in package")?;
|
||||
let mut host = crate::app::plugin_host::HostSession::new(app, 0);
|
||||
let process =
|
||||
ocs_plugin_api::process::PluginProcess::spawn(&path, &mut host).map_err(|e| e.to_string())?;
|
||||
let id = process.id().to_string();
|
||||
let name = process.manifest().name.clone();
|
||||
let module = ocs_plugin_api::ribbon::owned::to_shared_module(
|
||||
id.clone(),
|
||||
name,
|
||||
process.ribbon().to_vec(),
|
||||
);
|
||||
Ok(LoadedPlugin {
|
||||
process: Arc::new(process),
|
||||
module,
|
||||
id,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+21
-53
@@ -17,24 +17,15 @@ pub fn ribbon_modules_enabled(
|
||||
) -> Vec<Box<dyn CadModule>> {
|
||||
#[cfg_attr(target_arch = "wasm32", allow(unused_mut))]
|
||||
let mut core = core_registry::all_modules();
|
||||
// Dynamically-loaded external plugins contribute tabs (their libraries stay
|
||||
// resident for the session, so these vtables remain valid).
|
||||
// Dynamically-loaded external plugins contribute tabs via the crate manager.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let mut addons: Vec<(i32, Box<dyn CadModule>)> = Vec::new();
|
||||
crate::plugin::external::with_loaded(|loaded| {
|
||||
for lp in loaded {
|
||||
if disabled.contains(lp.id.as_str()) || !lp.process.is_alive() {
|
||||
continue;
|
||||
}
|
||||
addons.push((
|
||||
lp.process.manifest().ribbon_order,
|
||||
Box::new(lp.module.clone()) as Box<dyn CadModule>,
|
||||
));
|
||||
}
|
||||
crate::plugin::external::with_manager(|manager| {
|
||||
let addons = manager.ribbon_modules(|id| disabled.contains(id));
|
||||
core.extend(addons.into_iter().map(|(_, module)| {
|
||||
Box::new(module) as Box<dyn CadModule>
|
||||
}));
|
||||
});
|
||||
addons.sort_by_key(|(order, _)| *order);
|
||||
core.extend(addons.into_iter().map(|(_, ribbon)| ribbon));
|
||||
}
|
||||
let _ = disabled;
|
||||
core
|
||||
@@ -47,44 +38,20 @@ pub(crate) fn try_dispatch(app: &mut OpenCADStudio, tab: usize, cmd: &str) -> bo
|
||||
{
|
||||
use super::host::HostSession;
|
||||
let disabled = app.disabled_plugin_ids();
|
||||
let mut started: Option<(u64, std::sync::Arc<ocs_plugin_api::process::PluginProcess>)> = None;
|
||||
let mut dead_plugins: Vec<String> = Vec::new();
|
||||
let mut dispatch_errors: Vec<(String, String)> = Vec::new();
|
||||
let handled = crate::plugin::external::with_loaded(|loaded| {
|
||||
let result = {
|
||||
let mut host = HostSession::new(app, tab);
|
||||
for lp in loaded {
|
||||
if disabled.contains(lp.id.as_str()) {
|
||||
continue;
|
||||
}
|
||||
if !lp.process.is_alive() {
|
||||
dead_plugins.push(lp.id.clone());
|
||||
continue;
|
||||
}
|
||||
let process = std::sync::Arc::clone(&lp.process);
|
||||
let mut start = |command_id: u64| {
|
||||
started = Some((command_id, std::sync::Arc::clone(&process)));
|
||||
};
|
||||
match crate::plugin::guard("dispatch", || lp.process.dispatch(&mut host, cmd, &mut start)) {
|
||||
Some(Ok(true)) => return true,
|
||||
Some(Ok(false)) => {}
|
||||
Some(Err(e)) => {
|
||||
eprintln!("[plugin] dispatch error for '{}': {e}", lp.id);
|
||||
dispatch_errors.push((lp.id.clone(), e.to_string()));
|
||||
}
|
||||
None => {
|
||||
// Panic already logged by guard.
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
});
|
||||
for id in dead_plugins {
|
||||
crate::plugin::external::with_manager(|manager| {
|
||||
manager.dispatch(&mut host, cmd, |id| disabled.contains(id))
|
||||
})
|
||||
};
|
||||
|
||||
for id in result.dead_plugins {
|
||||
app.push_plugin_error(&format!("Plugin '{id}' process died; skipping dispatch"));
|
||||
}
|
||||
for (id, err) in dispatch_errors {
|
||||
for (id, err) in result.errors {
|
||||
app.push_plugin_error(&format!("Plugin '{id}' dispatch error: {err}"));
|
||||
}
|
||||
if let Some((command_id, process)) = started {
|
||||
if let Some((process, command_id)) = result.started {
|
||||
app.set_active_command(
|
||||
tab,
|
||||
Box::new(crate::app::plugin_host::PluginProcessInteractiveAdapter::new(
|
||||
@@ -93,12 +60,13 @@ pub(crate) fn try_dispatch(app: &mut OpenCADStudio, tab: usize, cmd: &str) -> bo
|
||||
)),
|
||||
);
|
||||
}
|
||||
if handled {
|
||||
return true;
|
||||
}
|
||||
return result.handled;
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
let _ = (app, tab, cmd);
|
||||
false
|
||||
}
|
||||
let _ = (app, tab, cmd);
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in new issue
Block a user