VSTs, MessageManagerLock, assertion failures/crashes

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!

Ah, that’s why! :slight_smile:
Windows Sleep() takes a millisecond argument. Unix sleep() sleeps for an amount of seconds.
http://linux.die.net/man/3/sleep
and usleep() sleeps for microseconds, that’s what I used:
http://linux.die.net/man/3/usleep

That’s why I used Thread::sleep instead - I always get confused by the different units that the platform-specific methods take.

Ah, I didn’t know that this method existed, thought you were calling unix sleep(). Alright then… :slight_smile: