forked from viewit/OrcaSlicer-KX
fix(macos): prevent use-after-free crash on quit in GLCanvas3D teardown
Plater is a wxWindow subclass holding a pImpl (unique_ptr<priv> p). On app
exit the member p is destroyed before the wxWindow base destructor runs
DestroyChildren(), so the child View3D/GLCanvas3D windows are torn down
*after* p has already been freed.
GLCanvas3D::~GLCanvas3D() -> reset_volumes() then reaches back into the dead
Plater via two callbacks, each dereferencing the freed p:
- Selection::clear() -> plater()->canvas3D() -> p->get_current_canvas3D()
- _set_warning_notification() -> plater()->get_notification_manager()
-> p->notification_manager
Which one fires depends on scene state (selection vs. volumes present), so
quitting with a model loaded reliably crashes with EXC_BAD_ACCESS.
Guard both callbacks with the existing wxGetApp().is_closing() flag (set in
MainFrame's close handler and GUI_App::shutdown(), both before
DoDelayedCleanup destroys the frame). Both calls are UI-only side effects
that are no-ops during shutdown, so normal-use behavior is unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -10060,6 +10060,12 @@ void GLCanvas3D::_set_warning_notification_if_needed(EWarning warning)
|
||||
|
||||
void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
|
||||
{
|
||||
// On app quit the Plater pImpl (p) is freed before its child GLCanvas3D windows are
|
||||
// destroyed; reset_volumes() -> _set_warning_notification() -> get_notification_manager()
|
||||
// would then dereference the freed p (use-after-free crash on exit). No warning
|
||||
// notifications are needed while the app is closing.
|
||||
if (wxGetApp().is_closing())
|
||||
return;
|
||||
using NotificationLevel = NotificationManager::NotificationLevel;
|
||||
enum ErrorType{
|
||||
PLATER_WARNING,
|
||||
|
||||
@ -760,6 +760,12 @@ void Selection::clear()
|
||||
#endif
|
||||
|
||||
// #et_FIXME fake KillFocus from sidebar
|
||||
// On app quit the Plater's child GLCanvas3D windows are destroyed by the wxWindow
|
||||
// base destructor *after* Plater's pImpl (p) has already been freed. reset_volumes()
|
||||
// -> Selection::clear() then reaches back into plater()->canvas3D(), a use-after-free
|
||||
// that crashes on exit. This is a UI-only redraw hint, so skip it while closing.
|
||||
if (wxGetApp().is_closing())
|
||||
return;
|
||||
wxGetApp().plater()->canvas3D()->handle_sidebar_focus_event("", false);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user