plugin: parallel release fetches, faster shutdown, uninstall runner stop

- Run Plugin Manager release fetches on OS threads so they execute in

  parallel instead of serialising on the async executor.

- Reduce V4Connection shutdown poll from 3s to 100ms; the runner is killed

  and reaped without blocking host shutdown.

- Add PluginManager::remove and PluginProcess::shutdown_and_wait so

  uninstall can stop the runner before deleting files, fixing Windows

  access-denied errors on plugin removal.

- Rebuild ribbon immediately after uninstall so the tab disappears.
This commit is contained in:
Sebastian committed 2026-08-14 20:18:59 +02:00
1 parent 285d35182c
commit 83d7a4d7db
6 files changed
+75 -7

No files matched your search

@@ -2,6 +2,7 @@
use std::path::Path;
use std::sync::Arc;
use std::time::Duration;
use crate::host::{HostApi, HostNotification, PluginNotification};
use crate::process::{PluginError, PluginProcess};
@@ -206,6 +207,21 @@ impl PluginManager {
out
}
/// Shut down and remove the plugin with `id` from the manager.
///
/// Returns `true` if a plugin with that id was found and removed. The
/// process is killed and waited on so its DLL is released; this is intended
/// for uninstall, where the caller needs the files to become deletable on
/// Windows.
pub fn remove(&mut self, id: &str) -> bool {
let Some(index) = self.plugins.iter().position(|p| p.process.id() == id) else {
return false;
};
let plugin = self.plugins.remove(index);
plugin.process.shutdown_and_wait(Duration::from_secs(5));
true
}
/// Begin asynchronous shutdown of every plugin process.
///
/// Kills every child synchronously on the calling thread and moves the
+5 -3
View File
@@ -302,10 +302,12 @@ impl V4Connection {
}
// Dropping the writer closes the socket. The reader thread will then
// exit and clear `alive`. Joining can deadlock if a plugin child
// inherited the socket handle, so we only poll briefly.
let deadline = Instant::now() + Duration::from_secs(3);
// inherited the socket handle, so we only poll briefly. The host process
// will kill the runner afterwards, so a long wait only delays shutdown
// without improving cleanup.
let deadline = Instant::now() + Duration::from_millis(100);
while self.shared.alive.load(Ordering::SeqCst) && Instant::now() < deadline {
std::thread::sleep(Duration::from_millis(50));
std::thread::sleep(Duration::from_millis(10));
}
let _ = self.reader_handle.lock().unwrap_or_else(|e| e.into_inner()).take();
}