Bind weak pointers to rt_slot events

This fixes a crash when deleting routes, while there are still
automation events queued for the route.

Specifically, SoloControl has a reference Soloable& _soloable; which
points to the parent route. A rt-event can still hold a valid shared
pointer to the SoloControl, even if the route is destroyed.
Calling SoloControl::actually_set_value is fine (the control still
exists due to the shared ptr), but then checking the parent route:
```
if (_soloable.is_safe() || !can_solo())
```
accesses the already deleted route, which causes a crash.

The solution implemented here is to not bind a shared_ptr to the
realtime event. However, since deletion of the route happens in the main
UI thread, there may or may not still be a race.
This commit is contained in:
Robin Gareus 2022-12-16 00:01:38 +01:00
parent c64869596e
commit 3b9a253a84
3 changed files with 17 additions and 7 deletions

View File

@ -2251,7 +2251,7 @@ private:
}
/* specialized version realtime "apply to set of controls" operations */
SessionEvent* get_rt_event (boost::shared_ptr<ControlList> cl, double arg, PBD::Controllable::GroupControlDisposition group_override) {
SessionEvent* get_rt_event (boost::shared_ptr<WeakControlList> cl, double arg, PBD::Controllable::GroupControlDisposition group_override) {
SessionEvent* ev = new SessionEvent (SessionEvent::RealTimeOperation, SessionEvent::Add, SessionEvent::Immediate, 0, 0.0);
ev->rt_slot = boost::bind (&Session::rt_set_controls, this, cl, arg, group_override);
ev->rt_return = Session::rt_cleanup;
@ -2260,7 +2260,7 @@ private:
return ev;
}
void rt_set_controls (boost::shared_ptr<ControlList>, double val, PBD::Controllable::GroupControlDisposition group_override);
void rt_set_controls (boost::shared_ptr<WeakControlList>, double val, PBD::Controllable::GroupControlDisposition group_override);
void rt_clear_all_solo_state (boost::shared_ptr<RouteList>, bool yn, PBD::Controllable::GroupControlDisposition group_override);
void setup_midi_machine_control ();

View File

@ -638,6 +638,7 @@ typedef std::list<boost::shared_ptr<Stripable> > StripableList;
typedef std::list<boost::weak_ptr <Route> > WeakRouteList;
typedef std::list<boost::weak_ptr <Stripable> > WeakStripableList;
typedef std::list<boost::shared_ptr<AutomationControl> > ControlList;
typedef std::list<boost::weak_ptr <AutomationControl> > WeakControlList;
typedef std::list<boost::shared_ptr<SlavableAutomationControl> > SlavableControlList;
typedef std::set <AutomationType> AutomationTypeSet;

View File

@ -71,12 +71,15 @@ Session::set_controls (boost::shared_ptr<ControlList> cl, double val, Controllab
}
#endif
boost::shared_ptr<WeakControlList> wcl (new WeakControlList);
for (ControlList::iterator ci = cl->begin(); ci != cl->end(); ++ci) {
/* as of july 2017 this is a no-op for everything except record enable */
(*ci)->pre_realtime_queue_stuff (val, gcd);
/* fill in weak pointer ctrl list */
wcl->push_back (*ci);
}
queue_event (get_rt_event (cl, val, gcd));
queue_event (get_rt_event (wcl, val, gcd));
}
void
@ -92,7 +95,7 @@ Session::set_control (boost::shared_ptr<AutomationControl> ac, double val, Contr
}
void
Session::rt_set_controls (boost::shared_ptr<ControlList> cl, double val, Controllable::GroupControlDisposition gcd)
Session::rt_set_controls (boost::shared_ptr<WeakControlList> cl, double val, Controllable::GroupControlDisposition gcd)
{
/* Note that we require that all controls in the ControlList are of the
same type.
@ -101,15 +104,21 @@ Session::rt_set_controls (boost::shared_ptr<ControlList> cl, double val, Control
return;
}
for (ControlList::iterator c = cl->begin(); c != cl->end(); ++c) {
(*c)->set_value (val, gcd);
AutomationType type = NullAutomation;
for (auto const& c : *cl) {
boost::shared_ptr<AutomationControl> ac = c.lock ();
if (ac) {
ac->set_value (val, gcd);
type = ac->desc().type;
}
}
/* some controls need global work to take place after they are set. Do
* that here.
*/
switch (cl->front()->parameter().type()) {
switch (type) {
case SoloAutomation:
update_route_solo_state ();
break;