USB IO: Or how I learned to stop worrying and love Java NIO

This may not be very relevant to many people, but if you’re unsure of when to use the NIO classes, or are having problems interfacing with USB in Java then I hope it helps a little.

I was tasked with debugging an annoying error this week, where a position sensor connected to a laptop via USB froze approximately once every 70 attempts. The IO was performed in Java, accessing the USB port as though it were a local file and reading in data byte by byte using a BufferedInputStream. Before any data could be received, a start byte was transmitted to the device. The reason for the freezing became apparent relatively quickly, the thread was blocked on a BufferedInputStream.read() call. There was never any exception thrown when sending the start byte so it was assumed to be a fault with the device. The first attempt to fix any problem then is to solve the cause of it. In this scenario that simply means don’t attempt to read in data if none is available. The InputStream.available() method is ideal for this case, it returns an estimate of the number of bytes available to read from the stream. A standard InputStream reading call thus waits until available() returns an integer greater than one. Unfortunately in my application calling this method threw IOExceptions, indicating it’s not compatible with my device.

If you can’t solve the root cause of the problem then the second best option is to handle any errors gracefully. So if the thread doesn’t respond for more than a set timeout period, terminate it. This however was easier said than done. My first approach was to try and close the InputStream, as this should cause the read() call to fail and throw an IOException (stream closed). However this seemed to not have any impact and the outer thread was blocked on the InputStream.close() call. Having seen that Thread.stop() is deprecated, I tried using Thread.interrupt(), which should cause the blocked thread to throw an InterruptedException. Again however, this interrupt call didn’t stop the blocked thread.

I then checked StackOverflow to see if anyone else had come across this issue but couldn’t find anyone who had a USB port mapped to a file and was being accessed in Java, yet alone with a blocked read. I asked a question myself which didn’t get many replies although one person suggested that if manually closing the stream didn’t work then there must be an issue with the underlying System calls and so not even Thread.stop() would help. I checked and they were right, Thread.stop() was of no use. I came across a situation where a person implemented a timeout read call using Futures (this answer here as below:

// Read data with timeout
Callable<Integer> readTask = new Callable<Integer>() {
    @Override
    public Integer call() throws Exception {
        return inputStream.read();
    }
};

// Normal read loop
while (readByte >= 0) {
    Future<Integer> future = executor.submit(readTask);
    readByte = future.get(1000, TimeUnit.MILLISECONDS);
}

This calls inputStream.read() and waits for 1 second before throwing a TimeoutException. This seemed ideal on paper but in practice didn’t work either. I realised that this was because the manner to clean up in the caught TimeoutException was by calling executor.shutdownNow(), which sounds final but actually just calls Thread.interrupt().

Therefore I realised I had to take the plunge and learn Java NIO. This package was introduced in Java 1.4 and provided a variety of new methods to interface with IO at a lower level, such as the new Channels interface. I’m not 100% sure on the main differences between how channels and streams work underneath, but channels use buffers and so allow you to move about in the buffer reading from different parts, while streams just enable you to read byte by byte from the stream. This means that channels give you more flexibility with reading data, but also more care must be taken. Java NIO is commonly referred to as “non-blocking” IO, but really this is only true for the SocketChannel. The class for interfacing with files, FileChannel, is still based on streams and thus can not read in a non-blocking mode. It wasn’t until Java 7 and the addition of AsynchronousFileChannels that non-blocking reads could be implemented with files.

Rather than returning primitive data, the read call of an AysnchronousFileChannel returns a Future instance, from which the data can be retrieved by calling AysnchronousFileChannel.get(). This method also allows for additional parameters to provide a timeout duration, if no data is received in this period then the channel is closed and a TimeoutException is thrown. This was ideal for my situation, as using a lower level IO method allowed for more controlled access over a stream of data as I could manage the buffer manually, and only waiting until there was data available to read. So the simple readByte = (byte) inputStream.read() call was expanded into

AsynchronousFileChannel afc = AsynchronousFileChannel.open(Paths.get(portName), StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.clear();
byte[] receive;
Future future;

while(!stop) {
	future = afc.read(buffer, 0);
	future.get(2000, TimeUnit.MILLISECONDS);
	                
	// flip from filling to emptying
	buffer.flip();
 	receive = new byte[ buffer.remaining() ];
	buffer.get(receive);
	buffer.clear();
}

This worked perfectly, if there was either an instance where no data was received, the TimeoutException was thrown and all necessary clean up was performed. Futhermore while I had to learn how to use ByteBuffers, they gave me far more control over the handling of the data. The device sent data in the form of a line of ASCII bytes detailing various measures ~15ms. With the input stream this was read byte by bte, sometimes receiving corrupted values. However with the channels this would read an entire line at a time with no errors. Of course then I had to iterate through the receive array to extract the individual data values but that was no bother.

This is quite a long winded explanation of a fix unique to my situation, but I hope it helps to briefly explain the advantages of the NIO classes and when you would consider using them over a traditional stream. Also I hope it helps anyone interfacing with USB in this way as I have not been able to find many examples of this, as most people seem to have USB devices that just map to serial COM ports, allowing for the use of the well known RXTX library.

Related

comments powered by Disqus