I’ve recently updated JUCE (master 8.0.12) and there are now JUCE_ASSERT_MESSAGE_THREAD happening when calling MidiOutput::openDevice() when there wasn’t previously (8.0.7).
The use case is to receive Midi Input messages, modify them and then send them on to a MidiOutput. The input messages are queued to be processed on a dedicated thread which then sends them to the MidiOutput. I want to avoid using the message thread to avoid any potential delays from other activity that might be happening. To prevent issues when devices are disconnected and to avoid locks, the Midi Outputs were opened on this dedicated thread which is now giving these assertions. It seems i would need to now add locks if I need to open the output on the message thread. Is there another approach around this that would avoid locks?
I think this use-case might not be very well-supported at the moment.
Looking at the implementation of ump::Output, when the device gets disconnected it’ll end up calling Output::Impl::disconnected() which modifies the native member. At the same time, Output::Impl::send() accesses native without synchronisation. This could lead to undefined behaviour if send is called from a background thread while the device is disconnecting on the main thread. We’ll need to make some changes in order to make this safer.
I don’t think it’s viable to avoid locking when creating the output on a background thread, since we need to query the currently-connected devices, which are managed by the Endpoints object which gets modified on the main thread. We’d need to add some kind of synchronisation (probably an internal lock) in order to make it safe to read the system state from a background thread.
We’ve pushed some documentation improvements to make it clearer which functions are suitable for use from a background thread. In the case of ump::Output, ideally you should create and destroy the output on the main thread, but send() can be called from a background thread.