VST2/VST3 conflict crash when creating editor


#1

My plugin works just fine when used separately as AU, VST2 & VST3.

But when I load all three into a host, there’s an interaction that causes a crash under circumstances. (I’ve tried the following in AudioPluginHost and Gig Performer)

  • Open and close the UI of the AU multiple times…all good.

  • Open the UI of the AU and VST2 in any order, multiple times…all good.

  • Open the UI of the AU and VST3 in any order, multiple times…all good.

  • Open the UI of the VST2, then open the VST3…CRASH.

  • Open the UI of the VST3, then open the VST2…CRASH.

  • Open the UI of the VST2, then open another VST2…all good.

  • Open the UI of the VST3, then open another VST3…all good.

So it seems that the AU is not involved…it’s just between some interaction between the VST2 & VST3.

The crash stack trace looks like this: (it crashes in the same place whether it’s the VST2 or VST3)

Thread 0 Crashed:: JUCE Message Thread  Dispatch queue: com.apple.main-thread

0   libobjc.A.dylib                0x00007fff62eacdbc objc_registerClassPair + 32

1   com.myaudio.myplugin              0x00000001126e92e8 juce::ObjCClass<NSView>::registerClass() + 24 (juce_osx_ObjCHelpers.h:244)

2   com.myaudio.myplugin              0x00000001126e6e4b juce::JuceNSViewClass::JuceNSViewClass() + 2043 (juce_mac_NSViewComponentPeer.mm:1627)

3   com.myaudio.myplugin              0x0000000112653305 juce::JuceNSViewClass::JuceNSViewClass() + 21 (juce_mac_NSViewComponentPeer.mm:1628)

4   com.myaudio.myplugin              0x0000000112653286 juce::NSViewComponentPeer::createViewInstance() + 54 (juce_mac_NSViewComponentPeer.mm:2092)

5   com.myaudio.myplugin              0x00000001126f0f56 juce::NSViewComponentPeer::NSViewComponentPeer(juce::Component&, int, NSView*) + 438 (juce_mac_NSViewComponentPeer.mm:83)

6   com.myaudio.myplugin              0x00000001126537ab juce::NSViewComponentPeer::NSViewComponentPeer(juce::Component&, int, NSView*) + 43 (juce_mac_NSViewComponentPeer.mm:194)

7   com.myaudio.myplugin              0x0000000112653755 juce::Component::createNewPeer(int, void*) + 85 (juce_mac_NSViewComponentPeer.mm:2206)

8   com.myaudio.myplugin              0x000000011254f123 juce::Component::addToDesktop(int, void*) + 1571 (juce_Component.cpp:623)

9   com.myaudio.myplugin              0x000000011223fde7 juce::attachComponentToWindowRefVST(juce::Component*, void*, bool) + 103 (juce_VST_Wrapper.mm:181)

10  com.myaudio.myplugin              0x0000000111dfcbc1 juce::JuceVST3EditController::JuceVST3Editor::attached(void*, char const*) + 625 (juce_VST3_Wrapper.cpp:854)

11  com.roli.juce.pluginhost       0x0000000102130e87 juce::VST3Classes::VST3PluginWindow::attachPluginWindow() + 327

12  com.roli.juce.pluginhost       0x00000001021301e9 juce::VST3Classes::VST3PluginWindow::componentVisibilityChanged() + 25

13  com.roli.juce.pluginhost       0x00000001024785a6 juce::ComponentMovementWatcher::componentVisibilityChanged(juce::Component&) + 134

14  com.roli.juce.pluginhost       0x00000001025da321 juce::Component::sendVisibilityChangeMessage()::$_2::operator()(juce::ComponentListener&) const + 33

15  com.roli.juce.pluginhost       0x0000000102428520 void juce::ListenerList<juce::ComponentListener, juce::Array<juce::ComponentListener*, juce::DummyCriticalSection, 0> >::callChecked<juce::Component::sendVisibilityChangeMessage()::$_2, juce::Component::BailOutChecker>(juce::Component::BailOutChecker const&, juce::Component::sendVisibilityChangeMessage()::$_2&&) + 128

16  com.roli.juce.pluginhost       0x000000010242823c juce::Component::sendVisibilityChangeMessage() + 108

17  com.roli.juce.pluginhost       0x0000000102427dfc juce::Component::setVisible(bool) + 508

18  com.roli.juce.pluginhost       0x0000000101f91264 PluginWindow::PluginWindow(juce::AudioProcessorGraph::Node*, PluginWindow::Type, juce::OwnedArray<PluginWindow, juce::DummyCriticalSection>&) + 1108

19  com.roli.juce.pluginhost       0x0000000101f8cdfb PluginWindow::PluginWindow(juce::AudioProcessorGraph::Node*, PluginWindow::Type, juce::OwnedArray<PluginWindow, juce::DummyCriticalSection>&) + 43

20  com.roli.juce.pluginhost       0x0000000101f8cba6 FilterGraph::getOrCreateWindowFor(juce::AudioProcessorGraph::Node*, PluginWindow::Type) + 710
...
...
...

So, it’s crashing in my plugin in the wrapper classes as it’s creating the editor, but before it actually it’s my code.

But if I do the same test with a simple plugin, it doesn’t crash, so somehow my plugin code is suspect in some way, but I have no idea how…and I have no idea how to debug this.

I’m on develop, pretty close to the current commit.

Any ideas?


#2

did you try doing that with debugger attached (and debug builds)?
you’d might get lucky with the generous assertions within the JUCE codebase.


#3

I also came to the conclusion that there must be some interaction. I’m having issues with plugin scaling if i’m loading VST2 and VST3 plugins in the same project (tested in Reaper OSX). I have re-sizable plugins. The VST3 UI scales to much in this case. But i didn’t have any crash so far.


#4

Definitely. Digging down images…

It tries to create a viewInstance

It dies trying to create the ivar…

Because somehow cls = 0x0

I don’t know what to make of this.


#5

After further investigation, it seems that this is failing.

I have no idea what part of my code would cause this to fail.

Anyone?


#6

Could someone from the JUCE team take a look at this please? I need some kind of clue as to what could be overlapping/interacting in the VST2 and VST3 wrappers.

I tried the same setup with another of my plugins (similar, but less complex) and it doesn’t crash like the one above. So obviously I’m causing the issue somehow, but I can’t figure out a way to debug this. I’ve tried turning on Asan and a few other debugging tools, but so far, nothing.


#7

I was not able to reproduce the issue in reaper with one of my plugins (latest develop). Maybe a problem in the hosts? Can you reproduce with this one (release 5.4.1)?

https://tal-software.com//downloads/plugins/TAL-Mod-installer.pkg


#8

If you can create a simple plug-in which reproduces the issue then it will help to track it down. I can’t get this to happen with any of the JUCE demo plug-ins or the template. Does it crash in all hosts or only specific ones?


#9

Unfortunately the way to debug is probably to make your more complex plug-in progressively simpler until the problem goes away, so you can hone in on the changes that cause it…


#10

Thanks for the replies.

@kunz @ed95 This doesn’t happen with any other plugins, and none of my other plugins…only the most complicated one of mine. I get the same crash in Juce AudioPluginHost and also Gig Performer (which also uses Juce). I’ll see if I can find another host that will load both and try again. At this point, I think @t0m is correct and I’ll have to try to dismantle/simplify my plugin until the problem goes away…which will be difficult given it’s complexity.


#11

Do you use version control? Is there an earlier iteration you could jump back to?


#12

The issue seems to go pretty far back version wise. I guess it just showed up because a user added two instances of my plugin that were VST2 and VST3…not an everyday occurrence.

Also, just tried in Reaper and got the same crash. So, it’s definitely something I’m doing. God knows what.


#13

Well, after quite a bit of digging, I found the problem…and man, was it a doozy…and completely out of the realm of what I was guessing.

If you’ve been reading along, my issue was that my plugin would crash the DAW if the user opened the UI of the VST2 and then the VST3 version (or reverse order) of my plugin in the same DAW project. It did not exhibit this behavior with the AU version.

The crash would occur when creating the NSView for my editor. It would never crash when the UI was unopened. It would never crash if the user opened and closed the same version of the plugin many times…only if opening the UI of either the VST2 or VST3, and then opening the other UI. This was very repeatable in JUCE’s AudioPluginHost, Gig Performer and Reaper on Mac.

It made sense to me that the cause of the crash was somewhere in my editor but it’s a fairly complicated editor with several pages and tons of controls and custom components so I wasn’t sure how to go about stripping it down. Instead, I decided to make a completely empty version of my editor…just painted a purple background. It still crashed! So it wasn’t in the UI code at all!

I started digging in my processor, mostly looking for memory allocations and accesses or maybe I’d accidentally used a static somewhere, but no. I’d already tried using the Address Sanitizer and every other sanitizer and memory tool in Xcode, but no luck. Little by little, I stripped everything away until I found the source of the crash. It was this line:

Random::getSystemRandom().setSeed(seed);

Unreal.

In prepareToPlay(), I use some random numbers to create a bunch of tables and for whatever reason, that one line (used several times) caused the crash. Simply changing it to use a local Random variable worked perfectly.

Some questions above my C++ knowledge and pay grade:

  • Why would that call interact between VST2 & VST3 and not AU?

  • Why would that call prevent an NSView from being created?

In all fairness, the documentation to Random::getSystemRandom() says that it’s not thread-safe, so I was probably using it in an unsafe way. I guess I’m not sure what “system” means in this case. My plugin? Everything running JUCE code on my computer at that moment?

Anyway, my apologies for the long post. I wanted to document this very odd issue in case anyone else runs into it.


#14

On macOS objective-c symbols are all global and visible. See the bottom of this page:

https://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/CppRuntimeEnv/Articles/SymbolVisibility.html

The issues you’re seeing are due to collisions of classes with the same name. To avoid these kind of things JUCE creates objective-c classes at runtime and randomises the class names.

Now Random::getSystemRandom returns a global shared random number generator, which JUCE also uses in its internals. If you set the seed of this generator so that it reverts back to one of its original states then you will end up reproducing the same subsequent “random” numbers and, as a consequence, the same objective-c class names.

Not an easy one to spot!


#15

Wow! That makes sense! By reseeding the global Random, I was affecting the code that randomizing the name of the Obj-C classes JUCE creates…including NSViews. Wow. And the reason I was setting that seed was so that while random, my numbers were repeatable…and so was the crash! lol.

I need a beer.


#16

Then perhaps it would be a good idea if the JUCE code used a different shared Random instance just for that naming purpose, so that developers resetting the seed of the global one for repeatibility reasons in their code don’t also end up with this (or related) issues


#17

However, I guess this is a promising nominee for “subtlest bug of 2018”!


#18

Yes, we could be safer here. However, the method is named getSystemRandom, so you shouldn’t be too surprised if the system misbehaves if you’re resetting the seed so the behaviour is no longer random…

It’ll mess up other things in different places like creating temporary files and random numbers in JUCE’s Javascript, which could be far harder to debug. I don’t want to create new random number generators for every place JUCE uses randomness internally, or have a duplicate, globally accessible, “system” generator that users should not touch.

I’ll make it very clear in the documentation not to reset things!


#19

Ok, fair point.

What about this, then:

void Random::setSeed (const int64 newSeed) noexcept
{
    if (this == &getSystemRandom())
    {
        jassertfalse;    // resetting the seed of the system instance is not allowed
        return;
    }
    seed = newSeed;
}

#20