Calling `getAvailableDevices` from within `handleIncomingMidiMessage` causes assertion failure

I’m working on a commandline app that will do routing and processing of midi data. The simple version of my problem is just like the title: I have an object that uses the handleIncomingMidiMessage method. If I call MidiInput::getAvailableDevices from within that codeblock I get an assertion failure at juce_mac_CoreMidi.cpp:259 ("JUCE_ASSERT_MESSAGE_THREAD").

The long version: if the handleIncomingMidiMessage codeblock calls the method of another object that then calls getAvailableDevices, I get the same assertion failure.

Is this expected behavior?

Is this happening because handleIncomingMidiMessage is already inherently running on the message thread, and thus it hangs because it obviously can’t assert message thread again? If so, how can I implement some sort of check to see what thread is in use?
I’m spitballing here. Help?

Your hitting the assertion because handleIncomingMidiMessage() is called on a different, high-priority system thread. If you read the documentation for the method it says:

"Receives an incoming message.

A MidiInput object will call this method when a midi event arrives. It’ll be
called on a high-priority system thread, so avoid doing anything time-consuming
in here, and avoid making any UI calls. You might find the MidiBuffer class helpful
for queueing incoming messages for use later."

Thank you so much. I see now, that totally makes sense.
It does lead me to another question however:

What would be the best way to mitigate this issue if I want to update my device list from within a handleIncomingMidiMessage block? Essentially, I need an incoming program change to trigger the activation or deactivation of different midi inputs and want to make sure the device list is current.

I’m totally new to working with threads, so while this may be obvious, I’m really not sure what my options are in this scenario. I’m guessing I would need to change the thread that’s being used inside the method that’s being called from within the handleIncomingMidiMessage block?

It sounds like you’d be better off updating the MIDI input/output lists periodically on a timer in the background rather than doing it in the handleIncomingMidiMessage method. Take a look at the MidiDemo to see how we do it there:

If for some reason you still want to do it in the callback, you could do something like the following:

void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) override
{
    // do some midi processing

    MessageManager::callAsync ([this] 
    {
        // update your device list here 
    });
}

which will call the supplied lambda some point later on the message thread.

Thank you so much! Looks like callAsync was exactly what I was looking for!

Spoke too soon. callAsync never seems to actually call the lambda? When should I expect it to execute? The documentation on callAsync is sparse, and appears to have an error. Documentation says it returns a bool, but it actually seems to return void.

I don’t particularly need the device list updated on the message thread, (which callAsync appears to do?). Is there a way to tell a method what thread to execute on? I’m calling the method of another object from within the handleIncomingMidiMessage block, which then calls the getMidiInputDevices.

I’d prefer not to update the list on a timer because I’d much rather just update right before I need it.

Do you have a message thread? If you’re using a command-line application you’ll need to set-up the message thread either by using a ScopedJuceInitialiser_GUI or calling MessageManager::getInstance() to instantiate the singleton.

Are you using the website documentation? This was added fairly recently to the develop branch so may be out of sync with the online docs.

Yes, just using the website documentation. Didn’t realize I wasn’t getting the whole picture.

Yep, definitely have a message thread. I instantiated a ScopedJuceInitializer_GUI as you mentioned, and other parts of the program have no problem calling getMidiInputDevices.

Also just realized I was confused earlier when I was talking about ‘not needing the device list updated on the message thread’. Of course I need it updated on the message thread, that’s what the whole problem is centered around. I’m goofy.

Anyway, thank you for your ongoing help. Hopefully we can get this solved.