Fix app close button and Metal crash on quit#864
Conversation
## Window Close Button Fix - Add `core:window:allow-destroy` and `core:window:allow-close` permissions to Tauri capabilities to fix "window.destroy not allowed" error when clicking the macOS red X close button ## Metal/llama-cpp Crash Fix - Replace `std::process::exit(0)` with `window.close()` in quit menu handler to trigger proper Tauri shutdown sequence - Switch from `.run()` to `.build().run()` pattern to handle RunEvent - Add `RunEvent::ExitRequested` handler that releases GPU context before exit - Add `release_gpu_context()` method to EmbeddingService that can be called on Arc<EmbeddingService> to drop LlamaContext without requiring &mut self - The LlamaContext holds Metal residency sets that must be released before the global LLAMA_BACKEND static is destroyed ## Technical Details The SIGABRT crash occurred during C++ static destruction (`__cxa_finalize_ranges`) when `ggml_metal_rsets_free` was called on resources still held by LlamaContext. By explicitly releasing the context during Tauri's exit sequence, we ensure Metal resources are freed before the global llama backend is destroyed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The previous fix only released the LlamaContext, but the LlamaModel also holds Metal residency sets that must be freed before the global LLAMA_BACKEND static is destroyed. Changes: - Handle WindowEvent::CloseRequested in addition to ExitRequested (ExitRequested may not fire on macOS - Tauri issue #9198) - Refactor EmbeddingService.state from Option<Mutex<LlamaState>> to Mutex<Option<LlamaState>> to allow taking ownership with &self - Update release_gpu_context() to drop the entire LlamaState (model + context), not just the context - Add release_gpu_resources() helper function in lib.rs The assertion failure was: GGML_ASSERT([rsets->data count] == 0) failed This occurred because the model still had registered residency sets when ggml_metal_device_free was called during static destruction. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Code Review ReportPR #864: Fix app close button and Metal crash on quit Requirements ValidationChecking against Issue #863 acceptance criteria: Window Close Button
Metal/llama-cpp Crash
All acceptance criteria satisfied. Code Review FindingsArchitecture & DesignThe implementation demonstrates excellent understanding of the root cause and provides a robust multi-layer defense strategy:
Detailed File Analysis
|
malibio
left a comment
There was a problem hiding this comment.
AI Code Review Complete - Recommendation: APPROVE
All acceptance criteria satisfied. This is a well-implemented fix with proper ownership patterns, defense-in-depth cleanup strategy, and thorough documentation. See detailed review comment above.
Review Feedback ResponseThank you for the thorough review and APPROVE recommendation! Addressing the Optional NitsBoth suggestions were evaluated and will be kept as-is: Nit 1:
|
* Fix app close button and Metal crash on quit (closes #863) ## Window Close Button Fix - Add `core:window:allow-destroy` and `core:window:allow-close` permissions to Tauri capabilities to fix "window.destroy not allowed" error when clicking the macOS red X close button ## Metal/llama-cpp Crash Fix - Replace `std::process::exit(0)` with `window.close()` in quit menu handler to trigger proper Tauri shutdown sequence - Switch from `.run()` to `.build().run()` pattern to handle RunEvent - Add `RunEvent::ExitRequested` handler that releases GPU context before exit - Add `release_gpu_context()` method to EmbeddingService that can be called on Arc<EmbeddingService> to drop LlamaContext without requiring &mut self - The LlamaContext holds Metal residency sets that must be released before the global LLAMA_BACKEND static is destroyed ## Technical Details The SIGABRT crash occurred during C++ static destruction (`__cxa_finalize_ranges`) when `ggml_metal_rsets_free` was called on resources still held by LlamaContext. By explicitly releasing the context during Tauri's exit sequence, we ensure Metal resources are freed before the global llama backend is destroyed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix: Release entire LlamaState (model + context) on app close The previous fix only released the LlamaContext, but the LlamaModel also holds Metal residency sets that must be freed before the global LLAMA_BACKEND static is destroyed. Changes: - Handle WindowEvent::CloseRequested in addition to ExitRequested (ExitRequested may not fire on macOS - Tauri issue #9198) - Refactor EmbeddingService.state from Option<Mutex<LlamaState>> to Mutex<Option<LlamaState>> to allow taking ownership with &self - Update release_gpu_context() to drop the entire LlamaState (model + context), not just the context - Add release_gpu_resources() helper function in lib.rs The assertion failure was: GGML_ASSERT([rsets->data count] == 0) failed This occurred because the model still had registered residency sets when ggml_metal_device_free was called during static destruction. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
* Fix app close button and Metal crash on quit (closes #863) ## Window Close Button Fix - Add `core:window:allow-destroy` and `core:window:allow-close` permissions to Tauri capabilities to fix "window.destroy not allowed" error when clicking the macOS red X close button ## Metal/llama-cpp Crash Fix - Replace `std::process::exit(0)` with `window.close()` in quit menu handler to trigger proper Tauri shutdown sequence - Switch from `.run()` to `.build().run()` pattern to handle RunEvent - Add `RunEvent::ExitRequested` handler that releases GPU context before exit - Add `release_gpu_context()` method to EmbeddingService that can be called on Arc<EmbeddingService> to drop LlamaContext without requiring &mut self - The LlamaContext holds Metal residency sets that must be released before the global LLAMA_BACKEND static is destroyed ## Technical Details The SIGABRT crash occurred during C++ static destruction (`__cxa_finalize_ranges`) when `ggml_metal_rsets_free` was called on resources still held by LlamaContext. By explicitly releasing the context during Tauri's exit sequence, we ensure Metal resources are freed before the global llama backend is destroyed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix: Release entire LlamaState (model + context) on app close The previous fix only released the LlamaContext, but the LlamaModel also holds Metal residency sets that must be freed before the global LLAMA_BACKEND static is destroyed. Changes: - Handle WindowEvent::CloseRequested in addition to ExitRequested (ExitRequested may not fire on macOS - Tauri issue #9198) - Refactor EmbeddingService.state from Option<Mutex<LlamaState>> to Mutex<Option<LlamaState>> to allow taking ownership with &self - Update release_gpu_context() to drop the entire LlamaState (model + context), not just the context - Add release_gpu_resources() helper function in lib.rs The assertion failure was: GGML_ASSERT([rsets->data count] == 0) failed This occurred because the model still had registered residency sets when ggml_metal_device_free was called during static destruction. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
* Fix app close button and Metal crash on quit (closes #863) ## Window Close Button Fix - Add `core:window:allow-destroy` and `core:window:allow-close` permissions to Tauri capabilities to fix "window.destroy not allowed" error when clicking the macOS red X close button ## Metal/llama-cpp Crash Fix - Replace `std::process::exit(0)` with `window.close()` in quit menu handler to trigger proper Tauri shutdown sequence - Switch from `.run()` to `.build().run()` pattern to handle RunEvent - Add `RunEvent::ExitRequested` handler that releases GPU context before exit - Add `release_gpu_context()` method to EmbeddingService that can be called on Arc<EmbeddingService> to drop LlamaContext without requiring &mut self - The LlamaContext holds Metal residency sets that must be released before the global LLAMA_BACKEND static is destroyed ## Technical Details The SIGABRT crash occurred during C++ static destruction (`__cxa_finalize_ranges`) when `ggml_metal_rsets_free` was called on resources still held by LlamaContext. By explicitly releasing the context during Tauri's exit sequence, we ensure Metal resources are freed before the global llama backend is destroyed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * Fix: Release entire LlamaState (model + context) on app close The previous fix only released the LlamaContext, but the LlamaModel also holds Metal residency sets that must be freed before the global LLAMA_BACKEND static is destroyed. Changes: - Handle WindowEvent::CloseRequested in addition to ExitRequested (ExitRequested may not fire on macOS - Tauri issue #9198) - Refactor EmbeddingService.state from Option<Mutex<LlamaState>> to Mutex<Option<LlamaState>> to allow taking ownership with &self - Update release_gpu_context() to drop the entire LlamaState (model + context), not just the context - Add release_gpu_resources() helper function in lib.rs The assertion failure was: GGML_ASSERT([rsets->data count] == 0) failed This occurred because the model still had registered residency sets when ggml_metal_device_free was called during static destruction. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
Summary
Closes #863
Changes
Window Close Button
core:window:allow-destroyandcore:window:allow-closepermissions to Tauri capabilitiesMetal/llama-cpp Crash Fix
Root Cause: The assertion
GGML_ASSERT([rsets->data count] == 0)failed because Metal residency sets were still registered whenggml_metal_device_freewas called during C++ static destruction (__cxa_finalize_ranges).Solution:
WindowEvent::CloseRequestedin addition toExitRequested- on macOS,ExitRequestedmay not fire reliably (Tauri issue #9198)LlamaState(model + context), not just the context - both hold Metal residency setsEmbeddingService.statefromOption<Mutex<LlamaState>>toMutex<Option<LlamaState>>to allow taking ownership with&self(needed since service is behindArc)std::process::exit(0)withwindow.close()in quit menu handlerTest Results
ggml_metal_free: deallocating🤖 Generated with Claude Code