JDK-8232862 : MidiInDevice thread stuck after closing device
  • Type: Bug
  • Component: client-libs
  • Sub-Component: javax.sound
  • Affected Version: 13
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: os_x
  • CPU: x86
  • Submitted: 2019-10-14
  • Updated: 2021-04-01
Description
ADDITIONAL SYSTEM INFORMATION :
OSX 10.14.6
openjdk version "13" 2019-09-17
OpenJDK Runtime Environment (build 13+33)
OpenJDK 64-Bit Server VM (build 13+33, mixed mode, sharing)

A DESCRIPTION OF THE PROBLEM :
When opening a MidiInDevice a new thread is created for listening for midi messages.
This thread calls a native method (MidiInDevice::nGetMessages
) for polling/fetching data. 
On osx this native method is blocking. If the device is now closed using MidiInDevice::close the native call will still block and the listener thread will be open for ever. This causes other side effects such as that the device can never be used again during the JVM runtime.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
0) Run on osx
1) Open a midi input device (not synthesizer, not sequencer)
2) Close the input device
3) The application will now not terminate anymore
The same code works on windows

The sample code does the steps listed above with 5 seconds sleep in between

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The application should terminate and no midi thread should be active.
ACTUAL -
A midi input thread is still active and the native call is blocking

---------- BEGIN SOURCE ----------
package test;

import java.util.Arrays;
import java.util.List;

import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Synthesizer;
import javax.sound.midi.Transmitter;

public class MidiTest implements Receiver
{
	private Transmitter transmitter;
	private MidiDevice device;

	public static void main(String[] args) throws MidiUnavailableException, InterruptedException
	{
		MidiTest test = new MidiTest();
		test.openDevice();
		Thread.sleep(5000);
		test.closeDevice();
		Thread.sleep(5000);
		for (Thread thread : Thread.getAllStackTraces().keySet())
		{
			System.out.println("Active threads: " + thread.getName());
		}
	}

	public void openDevice() throws MidiUnavailableException
	{
		List<MidiDevice.Info> deviceInfo = Arrays.asList(MidiSystem.getMidiDeviceInfo());
		for (MidiDevice.Info info : deviceInfo)
		{
			MidiDevice device = MidiSystem.getMidiDevice(info);
			if (device.getMaxTransmitters() == 0 || device instanceof Sequencer || device instanceof Synthesizer)
			{
				continue;
			}

			// Found an output device
			open(device);
			return;
		}
	}

	private void open(MidiDevice device) throws MidiUnavailableException
	{
		this.device = device;
		device.open();
		transmitter = device.getTransmitter();
		transmitter.setReceiver(this);
		System.out.println("Listening for data...");
	}

	private void closeDevice()
	{
		System.out.println("Closing device...");
		transmitter.close();
		device.close();
	}

	@Override
	public void send(MidiMessage message, long timeStamp)
	{
		System.out.println("Data received: " + message.toString());
	}

	@Override
	public void close()
	{
		// Nothing
	}
}


---------- END SOURCE ----------

FREQUENCY : always