This is easy to reproduce. I have a component that contains a slider. I drag the slider and while I’m dragging, I delete the component in the MessageManager thread, which calls the slider destructor. It will crash in Slider::Pimpl::sendDragEnd() when “this” becomes nullptr.
What I want to happen: force a stop to the dragging in the Slider, then delete the Slider immediately as normal.
If I can’t do that, then alternately: wait somehow, or set a flag, and then delete the component when the user stops dragging. This sounds complex and probably creates new bugs.
I would just change its visibility. If you absolutely need to destroy it then, in a timer, i would check if: the slider is not nullptr & is not visible & is not dragging. Then, i’ll destroy it.
I had the thought that show/hide might be the preference. It is not intuitive in this case, as there are ~50 different components that could be displayed in this area, and only one is displayed at any given time. So, think of something like an NI Guitar Rig rack that has many different completely different virtual UI’s that are dynamically inserted/removed by the user. Allocating all 50 feels bad, but I guess I’m realizing that the rules are different with components and UI where a user is interacting, and some of the common programming guidelines like saving memory don’t apply.
I did exactly as you indicate - I used a timer, and Component::isMouseDownAnywhere() to brute force a solution, but still thinking about show/hide.
It is actually a small hierarchy of components that I am deleting. Now that I look, I am not doing removeChild on the top component - that is a great idea. Maybe that solves it (?). Will try this in a few hours.
It seems that deleting the component removes the child from the parent automatically, so apparently it wouldn’t fix it. But if for some reason remove previously is necessary in your case, it would mean that it is not totally safe to delete the component in all cases without first removing it, as stated in the info.
Note that removing a child will not delete it! But it’s ok to delete a component without first removing it - doing so will automatically remove it and send out the appropriate notifications before the deletion completes. JUCE: Component Class Reference
Another alternative would be to only remove the component, and later when it is safe, delete it. maybe checking for components that have no parent
I also thought it was related to listeners, but it isn’t. There are no registered listeners - just a basic slider in a parent component that is destroyed. Apparently sendDragEnd() is always called when you drag a slider, whether there are listeners or not.
Can you share the exact code that exposes the problm?
A possible scenario is, if you syncronously delete the slider while being in a callback, this has to fail.
But if you wrap the deleting call in a MessageManager::callAsync() it should be fine.
I know that says main thread, but the MessageQueue made me think it was MessageManager, and also there is no other thread in the crash dump that mentions Message.
Easy enough to add a callAsync if I have misunderstood that.
Ah shame. FTR, my hypothesis wasn’t about a background thread. You are correct, the MessageQueue::deliverNextMessage is the MessageManager.
But if you trigger a delete in a callback and there are subsequent calls in that callback refering to this, they would have to fail.
By calling the callAsync, it would allow to finish the callback before the delete happens.
Looking into the mouseDown and the ~ScopedDragNotification(), there is a check if the slider wasn’t deleted. So it is weird that it still fails.
I have no further idea what’s going on.
I went through this with custom widgets and calls from other threads, and callAsync didn’t help either. I made my own “async” that just uses a timer and “moves” the call on the message thread. I use it as a member of the class that has to destroy the custom widgets. I’m not sure if it’ll help :