From d79ec11b85fbc893347e977df60a67c95a81af0a Mon Sep 17 00:00:00 2001 From: Aidan729 Date: Thu, 11 Jun 2026 22:52:41 -0400 Subject: [PATCH] Make the Windows handler reentrancy-safe (try_borrow_mut) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit handle_on_frame and handle_event hold self.handler.borrow_mut() across the entire user callback. If that callback synchronously triggers another window message — e.g. a SetWindowPos or host-driven resize that sends WM_SIZE straight back into the window procedure — the wndproc re-enters handle_event, borrows the handler a second time, and panics with a BorrowMutError. In an audio-plugin context that unwinds across the FFI boundary and crashes the host. Use ry_borrow_mut() and skip the re-entrant call instead of panicking. The handler isn't reentrant, so dropping the nested invocation is correct: window state stays consistent and the next genuine frame/event picks up the latest size. This makes it safe to call host resize APIs (which synchronously re-enter the window proc on Windows) from inside a handler callback. --- src/win/window.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/win/window.rs b/src/win/window.rs index 6788ae20..48c0ae8a 100644 --- a/src/win/window.rs +++ b/src/win/window.rs @@ -528,16 +528,25 @@ impl WindowState { } pub(crate) fn handle_on_frame(&self) { - let mut handler = self.handler.borrow_mut(); + // Use `try_borrow_mut` so a re-entrant call (e.g. a synchronous `WM_SIZE` + // dispatched by a `SetWindowPos`/host resize that happens *inside* the + // handler) is skipped instead of panicking with "already borrowed". + let Ok(mut handler) = self.handler.try_borrow_mut() else { + return; + }; let Some(handler) = handler.as_mut() else { return }; let mut window = crate::window::Window::new(Window { state: self }); handler.on_frame(&mut window) } - pub(crate) fn handle_event(&self, event: Event) -> EventStatus { - let mut handler = self.handler.borrow_mut(); - + pub(crate) fn handle_event(&self, event: Event) -> EventStatus { + // See `handle_on_frame`: skip re-entrant events rather than panicking on + // a double `borrow_mut`. This is what makes resizing from within a + // handler callback safe (the host's resize synchronously re-enters here). + let Ok(mut handler) = self.handler.try_borrow_mut() else { + return EventStatus::Ignored; + }; let Some(handler) = handler.as_mut() else { return EventStatus::Ignored; };