1

I've got a simple test application which connects to a USB device (as a local file), transmits a start transmission character, and then reads the input from the device. This all works fine, but infrequently it would block on the read() call. To prevent this I attempted to check whether there are at least 1 available bytes before proceeding with the read() operation. However when I run this I get an IOException from the available() call.

My code is:

public static void testRecord() {
    // Records data for a second
    String portName = "\\\\.\\<DEVICE_FILE>";
    FileWriter out = null;
    byte singleByte;
    long startTime = System.currentTimeMillis();
    try {

        out = new FileWriter(portName);
        out.write('c');     // start data transmission
        out.write(0x0d);    // carriage return required
        out.flush();
        if (out != null)
            out.close();

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(portName), 512);
        System.out.println("Connected to device");

        while(System.currentTimeMillis() < startTime+1000) {
            avail = bis.available();
            if (!(avail > 0)) {
                System.out.println("Available: " + avail);
                continue;
            }

            singleByte = (byte) bis.read();
            // Do stuff with data    
        }
        if (bis != null)
            bis.close();              
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        System.out.println("ERROR");
    }

}

The Error trace is:

Connected to device
java.io.IOException
    at java.io.FileInputStream.available(Native Method)
    at java.io.BufferedInputStream.available(Unknown Source)
    at test.Test.testRecord(Test.java:58)
    at test.Test.main(Test.java:135)
ERROR

Am I doing something wrong? If I comment out the available() call it works perfectly aside from the issue that it blocks on read() approximately once every 50 runs. I've also tried implementing the reader as a BufferedReader over a InputStreamReader (as the data is ASCII) and using the ready() method of BufferedReader but that always returns false.

EDIT: I'm looking at alternatives to checking if there is data available before reading, most of which involve handling a blocking read() call. I've got an outer thread checking for a timeout, but if there is a timeout, trying to stop the thread with thread.interrupt() still won't interrupt the blocking read() call. Likewise closing the InputStream from the outer thread doesn't work either as the call is still blocked. I also tried the solution here https://stackoverflow.com/a/9832633/1020006 but executor.shutdownNow() is just a wrapper for Thread.interrupt() and so has the same issue. I want to avoid using Thread.stop() as it's deprecated.

The only other choice I can see is rewriting the reading using a FileChannel which are asynchronously interruptible.

EDIT 2: I tried using Thread.stop() but this also didn't work, as pointed out by Peter Lawrey in the comments this is indicative behaviour of a system fault. I managed to get it all working by refactoring the reading into an AsynchronousFileChannel object, which returns a Future object from its read call. The data can be obtained from the Future with a timeout in the get() method. This then throws a TimeoutException which can be caught to close up the channel.

Community
  • 1
  • 1
Stuart Lacy
  • 1,963
  • 2
  • 18
  • 30
  • java.io is blocking API. Consider using java.nio instead, which is non-blocking – ponomandr Sep 06 '14 at 16:07
  • You are not doing anything wrong, this could be an implementation bug. – Marko Topolnik Sep 06 '14 at 16:10
  • // Do stuff with data - what stuff are you doing there? Make sure the stream is not closed somewhere in that code. – BatScream Sep 06 '14 at 16:35
  • @ponomandr java.nio is blocking for files. NIO2 is non-blocking but really it just blocking a background thread. – Peter Lawrey Sep 06 '14 at 17:38
  • I would assume the device you accessing doesn't support this operation. You don't appear to need this operation so I would write your read loop to not use it. – Peter Lawrey Sep 06 '14 at 17:38
  • @BatScream currently I'm just printing out the data, I'm not closing the stream until after the while loop – Stuart Lacy Sep 06 '14 at 17:57
  • @PeterLawrey So NIO2 wouldn't help with this? I need some sort of modification to the current read loop as mentioned it blocks on read() every so often. Is there a way to stop attempting to read if it's been blocked for a set time limit? – Stuart Lacy Sep 06 '14 at 17:58
  • What is the size of the file you read? Does bis.available() fail at the first call itself? – BatScream Sep 06 '14 at 18:37
  • @BatScream It's a USB stream which continuously transmits data byte by byte. Yes the exception is thrown at the first call of available() – Stuart Lacy Sep 06 '14 at 18:56
  • It appears your device only supports certain operations. If all else fails you could try Thread.stop() on the thread if it blocks to long (from another thread monitoring it) It's hack but if your device doesn't behave, it might be your only option. – Peter Lawrey Sep 07 '14 at 09:51
  • @PeterLawrey Since thread.stop() is deprecated I looked into using thread.interrupt() instead but this won't interrupt an InputStream.read() block. Neither will closing the InputStream from outside the thread. – Stuart Lacy Sep 08 '14 at 14:52
  • @Stu closing the stream externally would normally prevent the blocking and cause it to throw an IOException. If this doesn't work, you have a more serious problem in the system call (outside Java's control) this could mean that Thread.stop() won't work either. – Peter Lawrey Sep 09 '14 at 13:42
  • Nope Thread.stop() didn't work either. In the end I solved it by changing the method of reading the stream to an AsynchronousFileChannel and passing each read operation into a Future, which had a timeout on the get() method. Catching the TimeoutException allowed me to clear up (close channels etc...) and get it all working smoothly. Thanks for your help with this – Stuart Lacy Sep 09 '14 at 15:11

1 Answers1

0

Read the edits for the different methods I tried, but I managed to get it all working by refactoring the reading into an AsynchronousFileChannel object, which returns a Future object from its read call. The data can be obtained from the Future with a timeout in the get() method. This then throws a TimeoutException which can be caught to close up the channel.

Stuart Lacy
  • 1,963
  • 2
  • 18
  • 30