From bc7f25e52c1acd8f66f27a940f0f8dd14fe8b2f5 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 23 Aug 2023 12:17:05 -0600 Subject: [PATCH] libpbd: AbstractUI must call & destroy sigc::trackable callbacks in its destructor AbstractUI IS-A BaseUI IS-A EventLoop IS-A sigc::trackable If we have sent a call_slot() request to an EventLoop that has not executed when the object involved in the call_slot() functor is destroyed, we need to ensure that the request is invalidated. To do this, We register "notify" callbacks with the sigc::trackable that is a base class of the object involved in the functor given to call_slot(). sigc::trackable will call these "notify" callbacks from its destructor. So when the call_slot() functor's relevant object dies, and its sigc::trackable base class is destroyed, it will invoke all of its the "notify" callbacks, which will in turn call EventLoop::invalidate_request() and this hopefully marks all the queued call_slot() functors as "do not call". However, invalidate_request() requires a lock, and access to the lock is granted via a pure virtual, EventLoop::slot_invalidation_lock(). In the heirarchy cited above, this is implemented by AbstractUI. When we destroy an AbstractUI, ~AbstractUI() is called first, and this destroys the lock and changes the VTT so that ::slot_invalidation_lock() becomes a pure virtual again. Eventually we will call ~trackable() which in turns runs all the "notify" callbacks, and then removes them. But when these callbacks end up in EventLoop::invalidate_request(), we try to call ::slot_invalidation_lock() and C++ will abort because of its (now) pure virtual status. Therefore, we must invoke the "notify" callbacks before the ::slot_invalidation_lock() becomes pure, and that means inside ~AbstractUI, as an explicit call to trackable::notify_callbacks(). This has not appeared before (remarkably), but became an issue when the Launchpad Pro support code's main object (derived from MIDISurface and hence from AbstractUI) "failed" to use sub-objects for its various methods. So when it connects to, for example, the Session::RouteAddedOrRemoved signal, it is connecting itself (derived from a sigc::trackable). When the Launchpad Pro object is destroyed, it tries to invalidate all the call_slot() requests, but this requires access to an event loop lock - owned by the Launchpad Pro event loop, which is already destroyed! Other surfaces have generally avoided this by using other objects to provide methods of dealing with signals from libardour objects. --- libs/pbd/pbd/abstract_ui.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/libs/pbd/pbd/abstract_ui.cc b/libs/pbd/pbd/abstract_ui.cc index c0f580de9f..0c3bfa9dd8 100644 --- a/libs/pbd/pbd/abstract_ui.cc +++ b/libs/pbd/pbd/abstract_ui.cc @@ -100,6 +100,7 @@ AbstractUI::AbstractUI (const string& name) template AbstractUI::~AbstractUI () { + trackable::notify_callbacks (); } template void