VSTs, MessageManagerLock, assertion failures/crashes

Hi,

I have a problem with a VST developed with Juce. When using my plugin (.so, under Linux) in a host, I get lots of assertion failures like this:

~/code/Wolpertinger-juced> grep "JUCE Assertion failure" crashlog.txt | sort | uniq JUCE Assertion failure in /home/johannes/code/juced/juce/src/juce_appframework/gui/components/juce_Component.cpp, line 1302 JUCE Assertion failure in /home/johannes/code/juced/juce/src/juce_appframework/gui/components/juce_Component.cpp, line 1355 JUCE Assertion failure in /home/johannes/code/juced/juce/src/juce_appframework/gui/components/juce_Component.cpp, line 1797 JUCE Assertion failure in /home/johannes/code/juced/juce/src/juce_appframework/gui/components/juce_Component.cpp, line 221 JUCE Assertion failure in /home/johannes/code/juced/juce/src/juce_appframework/gui/components/juce_Component.cpp, line 642 JUCE Assertion failure in /home/johannes/code/juced/juce/src/juce_appframework/gui/components/juce_Component.cpp, line 991
Later, the host will eventually crash.
This happens in the Debug version. In the Release version it doesn’t seem to happen at the beginning, but after playing with the plugins for some time (half an hour or so) the host still crashes. Both Renoise 2 and Jost crash. It happens both with Juce (the original) and Juced (the modified version by kRAkEn/gORe).

Checking the messages, I found this comment in juce_Component.cpp:

//============================================================================== void Component::setBounds (int x, int y, int w, int h) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. checkMessageManagerIsLocked ...
This confuses me :slight_smile: Where should I use a MessageManagerLock? Why are these methods called from a thread other than the message thread? I am not creating any threads in my plugin.
I don’t get these assertion failure messages from the Nekobee plugin, but I don’t find any reference to MessageManagerLock in the Nekobee code. Any ideas what I am doing wrong…? Maybe someone has seen this before and could give me a hint what to do.

Here’s my guess (I’m sure Jules/someone else will have a better understanding) - I hope I’m not confusing the situation (if I’m wong, please let me know)

I think I had this issue a while ago when the filter of one of plugins tried to directly reflect something on the UI (e.g. tried to display the incomming waveform from ProcessBlock.

Presumably, the host might be implemented such that the UI stuff runs in a separate thread to the filter/ProcessBlock/param handling side… in which case this might present itself.

Thankfully, it’s quite an easy thing to “fix”:

Put one of these:

{
 const MessageManagerLock mmLock;

… your stuff

}

But it might be that it’s highlighting part of the plugin is calling functions between threads (I think of it as a bit like one program calling directly calling a function in the middle of another separate program, without knowing the operating state of each other ).

There might be a better way to transfer the data between the two threads in a clean way - e.g. a queue/buffer

Ok, first, a correction to my initial post… It also happens with other plugins, not just mine. :frowning: E. g. (from the log):

Renoise LOG> VSTPlugs: Searching for new VST plugins in '/home/johannes/bin/vst/'...
Renoise LOG> VstPlugs: Trying to instantiate /home/johannes/bin/vst/testing/nekobeevst_debug.so
JUCE v1.46
NekobeePlugin::NekobeePlugin
Renoise LOG> VstPlugs: Analyzing VST plugin 'kRAkEn/gORe: nekobeevst_debug', Unique ID:1785621296, Version:2400
JUCE Assertion failure in /home/johannes/code/juced/juce/src/juce_appframework/gui/components/juce_Component.cpp, line 720
JUCE Assertion failure in /home/johannes/code/juced/juce/src/juce_appframework/gui/components/juce_Component.cpp, line 221
JUCE Assertion failure in /home/johannes/code/juced/juce/src/juce_appframework/gui/components/juce_Component.cpp, line 1797
JUCE Assertion failure in /home/johannes/code/juced/juce/src/juce_appframework/gui/components/juce_Component.cpp, line 1302
[ ... more assertion failures, then Renoise hange ]

I didn’t notice this at first because I was using the Release versions, it happens only in Debug mode.

[quote]Dub:
I think I had this issue a while ago when the filter of one of plugins tried to directly reflect something on the UI (e.g. tried to display the incomming waveform from ProcessBlock.

Presumably, the host might be implemented such that the UI stuff runs in a separate thread to the filter/ProcessBlock/param handling side… in which case this might present itself.
[/quote]

I suspected something like that… The curious thing is, the assertion failures seem to happen already when loading/“analyzing” the plugin. ProcessBlock shouldn’t run there. The asserts happen in Component::toFront, Component::setVisible, Component::internalRepaint, Component::addChildComponent, Component::setBounds, etc. To me this looks like the Editor component being created, which is strange. Does that mean that the Editor is created in a different thread than the main message thread?

Edit: I tried the MessageManagerLock like suggested above. I inserted it in the createEditor() function like this:

AudioProcessorEditor* wolp::createEditor() { const MessageManagerLock mmLock; return new editor(this); }
Now, there are much fewer assertion messages when loading the plugin. So… This seems to suggest that the function somehow runs in the “wrong thread”?

OK, I think I had a similar issue (almost exactly - I even {looks away} blamed Juce! It was just a few weeks back on this very forum, I think! ) but in the end it was down to my initialization code leaking!

Try commenting (#if’ing) out initialization functionality (for me it was my serialization/de-serialization code had a problem - but it’d be worth looking carefully at anything which allocs / writes to memory) - also completely clean everything and rebuild it all - just in case.

At the moment I don’t blame anything in particular, I’m just trying to find out what’s going on…

It also happens with other plugins I tried, with almost identical assertion failure messages, so I doubt it’s my initialization code. I even completely replaced my synth and editor with empty placeholder code and I still get those asserts - less than before, but they’re still there!

Also, it does not happen when the plugins are compiled in standalone mode. Only when compiled with the VST wrapper.

I found out that the same actually happens with JuceDemoPlugin, so this is an easy way to reproduce the problem. Try the following:

  • use the latest Juce code from subversion, v1.46 behaves the same IIRC

  • fix /extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp so that the plugin compiles without unresolved external symbols, so it can be loaded by a host. At line 254, insert this:

  • do whatever is necessary to get JuceDemoPlugin to compile. I compile with Code::Blocks, using VST SDK 2.4 and the static Juce library (not the amalgamated version).

  • be sure that the plugin gets compiled in Debug mode

  • Use a native Linux VST host (e. g. Jost or Renoise to load libJuceDemoPlugin.so

  • See output similar to the following

JUCE v1.46 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 602 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 221 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1679 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1184 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 524 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1679 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1679 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 873 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 221 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1679 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1184 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 524 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1679 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1679 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1679 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1679 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1184 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 524 JUCE Assertion failure in /home/johannes/code/juce-svn/trunk/src/juce_appframework/gui/components/juce_Component.cpp, line 1184 [.... more assertion failures ...]

  • Unload and load the plugin a few times. In case of Jost, it will crash. Renoise seems to take more time, but eventually it will also crash.

bump…

No reply from Jules yet… Is it not important to fix this because it’s Linux specific?

Sorry - the main reason I’ve not got an answer is that I don’t even have a linux vst build environment to try it out! All the linux vst porting has been contributed by other people.

But is this with the latest tip from SVN? I’d have expected that some message manager changes I made a couple of months ago would have eliminated the need to use any messagemanagerlocks in this code.

Yes, it occurs with the latest tip from SVN. The main problem seems to be that under Linux, a separate thread is created for running the dispatch loop (it’s in the SharedMessageThread class in juce_VST_Wrapper.cpp). Then the main thread calls Juce functions which require the message thread to be locked, which it isn’t, therefore the assertion failures.
I have a workaround which seems to be mostly working, but it needs changes to the juce_VST_Wrapper.cpp as well as to the plugin code. It involves using a MessageManagerLock in every place where the plugin or the VST wrapper accesses Juce, and it still doesn’t work perfectly: trying to addChangeListener() from a different thread crashes the host with an assertion failure in pthreads, even if the message thread is locked.
I’m working on a fix which would only need changes to juce_VST_Wrapper.cpp. If I find such a solution, will you include it?

Sure, I’ll include a solution if it seems like the right approach.

The fact that the message loop is on a thread should be ok because it calls setCurrentMessageThread() - so any calls made on that thread don’t need to be locked. I guess if the host is using another thread to call things, then any of those calls would need an mmlock around them.

Yes, it seems that’s the problem. Now I have modified the VST wrapper so that the plugins don’t have to be changed. I have used callFunctionOnMessageThread() which I guess is the most compatible thing to do, and it works, but it complicates the VST wrapper code a bit. Is it always OK to call any Juce code from a thread other than the message thread, while the message thread is locked? If so, I will use MessageManagerLocks instead.

BTW: the addChangeListener thing was a bug in my plugin code, that’s fixed now.

Yes, should be fine to do that.

Ok, then this is the fix I’m proposing, using mmlocks. There was a race condition at startup which should also be gone now.

--- juce_VST_Wrapper.cpp	2009-07-25 17:19:24.000000000 +0200
+++ /home/johannes/code/Wolpertinger-juced/vst/juce_VST_Wrapper.cpp	2009-08-02 12:29:00.000000000 +0200
@@ -219,11 +220,16 @@ static HWND findMDIParentOf (HWND w)
 
 class SharedMessageThread : public Thread
 {
+private:
+    bool initialized;
+
 public:
     SharedMessageThread()
-      : Thread (T("VstMessageThread"))
+      : Thread (T("VstMessageThread")), initialized(false)
     {
         startThread (7);
+        while(!initialized)
+            usleep(100000); // wait for thread intialization to avoid a race condition
     }
 
     ~SharedMessageThread()
@@ -241,6 +247,10 @@ public:
         const Thread::ThreadID originalThreadId = messageManager->getCurrentMessageThread();
         messageManager->setCurrentMessageThread (Thread::getCurrentThreadId());
 
+        initialiseJuce_GUI();
+
+        initialized= true;
+
         while ((! threadShouldExit()) && messageManager->runDispatchLoopUntil (250))
         {
         }
@@ -446,6 +456,7 @@ public:
     {
         if (editorComp == 0)
         {
+            const MessageManagerLock mmlock;
             AudioProcessorEditor* const ed = filter->createEditorIfNeeded();
 
             if (ed != 0)
@@ -462,6 +473,7 @@ public:
 
     void close()
     {
+        const MessageManagerLock mmlock;
         jassert (! recursionCheck);
 
         stopTimer();
@@ -1179,6 +1191,7 @@ public:
                 if (canDeleteLaterIfModal)
                 {
                     shouldDeleteEditor = true;
+                    recursionCheck = false; // KRAKEN
                     return;
                 }
             }
@@ -1219,6 +1232,7 @@ public:
         }
         else if (opCode == effEditOpen)
         {
+            const MessageManagerLock mmlock;
             jassert (! recursionCheck);
 
             deleteEditor (true);
@@ -1253,11 +1267,13 @@ public:
         }
         else if (opCode == effEditClose)
         {
+            const MessageManagerLock mmlock;
             deleteEditor (true);
             return 0;
         }
         else if (opCode == effEditGetRect)
         {
+            const MessageManagerLock mmlock;
             createEditorComp();
 
             if (editorComp != 0)
@@ -1490,7 +1506,6 @@ extern "C" __attribute__ ((visibility("d
 
 extern "C" AEffect* VSTPluginMain (audioMasterCallback audioMaster)
 {
-    initialiseJuce_GUI();
     SharedMessageThread::getInstance();
 
     return pluginEntryPoint (audioMaster);

If you prefer the whole file I can mail it to you.

Ok, that all seems to make sense - thanks!

I forgot: You still have to insert the juce_ImplementSingleton macro somewhere. This change didn’t show up in the diff because I already changed it in my local svn tree. The code around lines 256 or so (at the end of class SharedMessageThread) should look like this:

[code] juce_DeclareSingleton (SharedMessageThread, false)
};

juce_ImplementSingleton(SharedMessageThread)
#endif[/code]

else the plugins won’t be loadable.

Yes, thanks - I’d spotted that already. Will check in this and some other changes shortly.

Thanks for checking it in, looks good. Except one little thing: you do sleep(1); while waiting for the thread. I think waiting in steps of 1/10th of a second with usleep(100000) is enough, it creates less delay.

I wouldn’t have thought it’d make a measurable difference - sleep times are wildly inaccurate anyway.

“Wildly inaccurate” in the range of milliseconds or so. Less if you use a realtime kernel and/or a higher tick rate, which you should if you’re doing serious live audio stuff. Anyway, it’s no big deal really, but it makes the user wait at least a second for each different VST, so with many VSTs, it will make a difference.

Er… sleep(1) is 1 ms, not one second!