Just chiming in here to say I’ve been experiencing the same thing - seems like a race condition for sure, I just can’t tell what other thread is trying to destroy the listener list. I’m getting it pretty reliably on every shutdown, unless I breakpoint somewhere in the AudioProcessor destructor in which case I only hit the error sometimes.
Might have been said already since this thread is already pretty bug, but I was always able to tackle those problems with the UBSAN and ASAN from Xcode. The error message is sometimes a little cryptic and might be hard to read, but don’t let that discourage you! If you take the time to reconstruct what happened by the stack traces, I always found the bug or race condition.
For what it’s worth, I just encountered this bug and found something that fixed it (something besides commenting out the body of WrappedIterator::forEach).
I had declared some new sliders in my AudioProcessorEditor.h file after the section where I declared their attachments. The crash happened every time I shut down the standalone plugin. I built the VST3 plugin and tested in a couple of DAWS and just closing the editor window caused a crash. I landed here in this thread and tried all kinds of things.
Anyway, moving the declaration of the new sliders above that of their corresponding attachments seems to have fixed it. Hoping this might work for someone else or indicate why it’s happening.
Yes, and that makes perfect sense, because the Attachment unregisters it’s Slider Listener on destruction
I always found that a bit challenging at times, that the order of variables in a class makes an important difference, were as the order of methods e.g. don’t.
Nice one! Yep. Classic mistake, I’m sure. Turns out this is exactly what was causing my issue; SliderAttachment was declared above, because it’s part of “public” declarations. Just added another “public” section to the bottom of the class. Problem solved
I just wrote a thread on this issue, not sure why I didn’t find this before I posted as I’ve been looking for a solution…
I’ve tried everything in this thread, I always have my attachments at the bottom, so it isn’t that, I’ve added default deconstructors to classes that didn’t have one. I’m unable to get Address Sanitizer (Xcode) to work with standalone export, it worked the 1st time & now givesbthe not installed error, I’ve tried adding the given line to the ‘arguments passed on launch’ box in the scheme, but still has the same error.
So right now I’m commenting out the WrappedIterator::foreach and about test to see if it fixes it, which is obviously a horrible solution and not plausible for the long term.
I’m curious how others on this thread fixed it in their projects? It’s a total showstopper.
I couldn’t get to the bottom of it so just deleted the v7 version and use the version from Juce 6, problem solved.
Thanks for letting me know, you reverted to v6 completely? I’ve spent days on it with no luck, I’ve made sure everything is wrapped in safe pointers, messed around with member order and no luck. The worst thing is that it doesn’t crash consistently and it’s a different class that precedes the crash almost every time.
No just the ListenerList file, the one from v6 drops in and works in v7 without problems (with the caveat that I’m a couple of point releases behind on v7 now).
I had a little play with this and one way I can reproduce an issue that seems similar to the one described here was in a multi-threaded environment where the callback is happening on a thread separate from the destructor or the removeListener calls.
There’s a couple of ways I think we could check for this
- (Recommended) use the thread sanitiser
- Create each ListenerList like so
juce::ListenerList<Listener, juce::Array<Listener*, juce::CriticalSection>> listeners;
This should work in most cases but it is somewhat dependant on how the ListenerList is being used. - Create a
std::mutex
and ensure it’s locked whenever the listeners member or any listener is being accessed. this requires careful attention to detail!
I’m not 100% convinced this is the problem but it seems worth eliminating as a potential source.
Thanks very much for the reply but that’s above my JUCE capabilities tbh, I’m not doing anything fancy with threads, in the editor it’s standard apart from I do use a couple of timers in a class (there are 2 of these) that draw waveforms using the JUCE waveform class and my own play marker code. So I basically have the processor & the editor and haven’t created any extra threads in either processor or editor. The only listeners I use are ValueTree, APVT & a couple of mouse Listeners.
I’ve been thinking of ways around it, because I don’t have issues with APVT listeners, just VT, so perhaps I can make dummy parameters and use those as a replacement for the VT listeners. A bit of a pain, VT’s are great but without listeners they’re not so useful.
Tomorrow or Sunday I plan to try and persevere to get thread sanitizer working in Xcode, or if not I’ll try on a windows machine. Thread sanitizer gives the too late message and adding the provided startup command it gives, I don’t know how to get that working. I assumed it would be in the scheme arguments but it didn’t make any difference.
No chance one or more of these is a HighResolutionTimer
?
I inherit juce::Timer and use juce::startTimerHz(60); and have Timer::stopTimer(); in the deconstructor.
EDIT: the timer itself is just calling repaint(); which in turn calls my Play Markers function.
EDIT2: I just commented out the timer start/stop and still got a shutdown crash.
I managed to get thread sanitizer running & it reported a race condition on startup, I’m not sure how to diagnose it from the info it gave, but at least I know it is that. I tried using a mutex for each of the removeListener’s and it didn’t help, still crashing when closing.
So I thought I’d give juce 6 a try but I’m using juce 7 specific code such as the Parameter info and it would be painful to change. So I grabbed the juce_ListenerList.h from 6 replaced the JUCE 7 h/cpp and it looks like the problem has gone completely. It’s early days but I’ve been testing for about 20 minutes & haven’t had a shutdown crash yet, and sanitizer hasn’t reported anything. This is obviously not a great solution & I’d like it solved, but at least for now I can release without the crashes, fingers crossed.
I’m happy to help in any way to get the issue addressed in juce 7, but although I have a good understanding of basic C++, but there are still areas I’ve never ventured and so would need more information on how to do things perhaps more experienced programmers would take for granted.
Would you mind sharing the output, either here or privately?
The mutex would have to be locked when adding, removing, calling back, or any time the listeners list is accessed in any way.
Interesting, I expected the thread sanitiser might still hit but it just wouldn’t result in a crash.
I’ll see if in my own experiments I can reproduce the same behaviour.
If you are using Xcode (I think I read that above), it is worth investing the time into getting the address sanatizer and thread sanatizer to work. The solve and find all kinds of problems, that are gonna bust you maybe in a year from now.
AND: In my early experiences I found it “Apple Like” to be very easy to use them. You should have a Standalone Plugin Scheme. Set that to debug and enable either address or thread sanatizer in the diagnostics tab. You can use them both at the same time, but you can always at the undefined behaviour checker (you should). Then hit CMD+R to build and run the application. Changing the sanatizers requires rebuilding the entire source, so this is gonna take some time with JUCE in the back (maybe one minute on an average machine).
Thanks for that, I did actually manage to get the sanitizer working with the standalone, I’m not sure why it didn’t work the last time I tried but perhaps it was because I hadn’t done a clean, that makes sense. On Anthony’s suggestion I did find a race condition, however I’m not really sure how to interpret the results. I send it via PM to Anthony & have offered to send my entire project if it will help diagnose/solve the issue.
For now I’m sticking with the v6 juce_listListener.h it has without a doubt solved the problem for me, I worked on it extensively all day yesterday and there are zero issues anymore. I guess there is something in my code that’s creating the recipe, but I personally think at this it’s something in the JUCE listener code that’s the problem.
When the sanitizer says it’s running too late and gives the startup flag to use, I don’t suppose you know where you paste this? I tried it in arguments but it didn’t seem to have any effect when I was having that issue.
I don’t know what you mean by “running too late”
@anthony-nicholls I had a closer look at the ListenerList now, and I have to say, I think this WrappedIterator
stuff, was a bad design decision. The ListenerList is now not thread-safe anymore for simply calling all Listeners, which it was previously. This should have been wrapped into a different class and not propagated into the entire code-base of ValueTree etc. I think it is reasonably fine for the UI code, as this is designed for the message thread only and there are a hole lot of asserts anyway. But all other parts of the code (and maybe user-code using this class) is now possible compromised.
This change is also not listed in breaking changes, another huge oversight IMHO.
Last, I can’t make any sense of why this pattern is not also used in this function: https://github.com/juce-framework/JUCE/blob/a7d1e61a5511875dc8f345816fca94042ce074fb/modules/juce_core/containers/juce_ListenerList.h#L288
It gives a message in the console saying that it is running too late and provides startup flags to use. If you haven’t seen it then it’s likely not an issue for me anyhow now it is working.