BUG - assert in AudioPluginHost when selecting Midi Output

JUCE 6.0.3, OS X Mojave, Xcode

This seems a bug because it doesn’t matter if I have any project or plugin loaded into it or not.

This happens when I “run” the APH as an attached process to my plugin, but I have not actually instantiated my plug in.

  • run the project, it launches the Audio Plugin Host in debug mode.
  • double-click the Audio Output to open the Audio Settings.
  • select some other midi output; I have about 8 of them.
  • hits an assert at catch(…) “Your run() method mustn’t throw any exceptions!”:
void Thread::threadEntryPoint()
{
    const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder());
    currentThreadHolder->value = this;

    if (threadName.isNotEmpty())
        setCurrentThreadName (threadName);

    if (startSuspensionEvent.wait (10000))
    {
        jassert (getCurrentThreadId() == threadId.get());

        if (affinityMask != 0)
            setCurrentThreadAffinityMask (affinityMask);

        try
        {
            run();
        }
        catch (...)
        {
            jassertfalse; // Your run() method mustn't throw any exceptions!
        }
    }

    currentThreadHolder->value.releaseCurrentThreadStorage();

    // Once closeThreadHandle is called this class may be deleted by a different
    // thread, so we need to store deleteOnThreadEnd in a local variable.
    auto shouldDeleteThis = deleteOnThreadEnd;
    closeThreadHandle();

    if (shouldDeleteThis)
        delete this;
}

bump? Nobody else? I can make this hit nearly every time…

Do you have a stack trace?

This is where it’s hitting, but it doesn’t tell me much…
Screen Shot 2020-11-03 at 11.21.07 AM

right… the catch caught something that happened, but the thing that happened isn’t on the stack anymore… :frowning:

Yeah unfortunately that stack trace isn’t much use. I’m not sure this is a bug in JUCE though as I’m unable to reproduce it. To narrow things down a bit:

  • First of all, make sure you are on the latest tip of the develop branch
  • Do you hit this assertion if you just run a debug build of the plug-in host without running it as the debug executable of your plug-in?
  • Is it only happening on certain MIDI output devices, or all of them? I tested with MIDI Monitor as the output and it was able to receive MIDI messages from the host without any assertions

Thanks ed. I finally got a chance to get back to this. I downloaded the latest develop branch, recompiled the Projucer, rebuilt the AudioPluginHost, and it still does it.

  • yes, I hit this assertion if I just run a debug build of the plug-in host without running it as the debug executable of my plug-in;

  • I have a number of MIDI ports and devices attached, as you can see. It seems to happen always the first time I select a new MIDI Output from the combobox menu. In other words, I launch it and it has whatever was last selected as the MIDI output. I select any other one, and it hits the assert. I hit Continue in Xcode, and then it doesn’t do it anymore when changing outputs (does the assert only function one time?) If I then quit the APH, and relaunch, it comes up with my different MIDI output as the current selection, and then the first change to some other one will hit the assert. It does not, therefore, seem to be dependent on which port is actually current and which port is being selected.

Anything else I can do to assist this?

Showing the MIDI Output popup menu open:

You can make a quick edit to Thread::threadEntryPoint to find the root cause:

void Thread::threadEntryPoint()
{
    const CurrentThreadHolder::Ptr currentThreadHolder (getCurrentThreadHolder());
    currentThreadHolder->value = this;

    if (threadName.isNotEmpty())
        setCurrentThreadName (threadName);

    if (startSuspensionEvent.wait (10000))
    {
        jassert (getCurrentThreadId() == threadId.get());

        if (affinityMask != 0)
            setCurrentThreadAffinityMask (affinityMask);

//        try
//        {
            run();
//        }
//        catch (...)
//        {
//            jassertfalse; // Your run() method mustn't throw any exceptions!
//        }
    }

    currentThreadHolder->value.releaseCurrentThreadStorage();

    // Once closeThreadHandle is called this class may be deleted by a different
    // thread, so we need to store deleteOnThreadEnd in a local variable.
    auto shouldDeleteThis = deleteOnThreadEnd;
    closeThreadHandle();

    if (shouldDeleteThis)
        delete this;
}

Remember to put it back as you found it afterwards, but this will stop the debugger at the cause of the exception rather than at the jassert in the catch clause, which might give some more insight.

1 Like

Thanks for the code change. That does indeed trap it where the problem occurs. This is the stack trace now:

What is the system error signal message saying though?
It seems to happen when trying to lock the mutex (see stack frame 11, 12) causing one of possible: maybe a deadlock or maybe the mutex _m member null or deleted or not initialised yet?

It says:

Multi-Output Device (7): signal SIGABRT

And in the debug log at the bottom:

libc++abi.dylib: terminating with uncaught exception of type std::__1::system_error: mutex lock failed: Invalid argument

It seems that either your mutex is not allocated yet or already deleted, but I don’t know why.
If you navigate through the stack in debug mode you might be able to hover on the _m member on the stack before it crashes when calling lock(), the pointer value could tell you if it is unallocated or deleted. (e.g. in Windows you get 0xdddddddd for deleted or 0xcdcdcdcd for unitialised in debug mode).
Is it possible that your plugin would deallocate while still running the main thread loop ?

Thanks, but this is not “my plugin”. This is the JUCE AudioPluginHost being run, by itself, in Debug Mode. Hence the title of this thread.

But unless I miss something: even if not yours, one or more plugins is there otherwise it would not crash inside from the run() virtual method ?
One thing I would test is to temporarily remove all my plugins both in ~/Library/… and /Library/… (simply move them to a backup folder) make sure you don’t have any of them left in AU, VST, VST3 folders. What happens then do you still have any crash. Then if it does not crash, move them back progressively and identify which ones cause problems. Or it may also be an issue with your MIDI drivers.

I think you may be missing something… :wink:

I removed all the plugins, there are none. I emptied the plugin list in AudioPluginHost. There is nothing except the internal plugins.

It still crashes in the same exact way, in the exact same place. This is internal to the APH. Nothing to do with any third-party plugins.

I have 3 different company’s external MIDI Devices attached, and all 3 of them hit the assert in the same way.

This appears to be a bug in JUCE. I can arrive at no other conclusion.

Interesting thanks for confirming about the plugins, using the latest version (6.0.4) I could not reproduce your problem hence my skepticism, but you might be just right on this.

Are you running a Mac? Specifically, I am using Mojave 10.14.6.

Running max osx mojave 10.14.6, Not even removing my plugins, and adding my new midi interface electronic custom 16x16 design on the top of my keystation midi device and motu midi (20 devices attached all in all) then selecting different audio ports too, it works for me:

Did you try to run this under a new osx user login, does it make any difference?

I have determined that this is related to the selected Audio Output at the top being “Multi-Output Device”.

This is a special combined audio device you can create in the Mac Audio/MIDI Setup, and stack several audio devices together.

When this is selected as the current Audio Output on launching the AudioPluginHost, then changing the MIDI Output as I have described here causes it to hit the assert.

When some other single audio output is selected as the current when launching the APH, changing the MIDI Output does NOT hit the assert.

Earlier I said it did not happen in JUCE 5.4.7, but that is not true. It does, so this is nothing new. Just a peculiar situation with this sort of combined output device.

1 Like