Search code examples
javanio

SeekableByteChannel.read() always returns 0, InputStream is fine


We have a data file for which we need to generate a CRC. (As a placeholder, I'm using CRC32 while the others figure out what CRC polynomial they actually want.) This code seems like it ought to work:

broken:

Path in = ......;

try (SeekableByteChannel reading =
    Files.newByteChannel (in, StandardOpenOption.READ))
{
System.err.println("byte channel is a " + reading.getClass().getName() +
  " from " + in + " of size " + reading.size() + " and isopen=" + reading.isOpen());
    java.util.zip.CRC32 placeholder = new java.util.zip.CRC32();
    ByteBuffer buffer = ByteBuffer.allocate (reasonable_buffer_size);

    int bytesread = 0;
    int loops = 0;
    while ((bytesread = reading.read(buffer)) > 0) {
        byte[] raw = buffer.array();
System.err.println("Claims to have read " + bytesread + " bytes, have buffer of size " + raw.length + ", updating CRC");
        placeholder.update(raw);
        loops++;
        buffer.clear();
    }
    // do stuff with placeholder.getValue()
}
catch (all the things that go wrong with opening files) {
    and handle them;
}

The System.err and loops stuff is just for debugging; we don't actually care how many times it takes. The output is:

byte channel is a sun.nio.ch.FileChannelImpl from C:\working\tmp\ls2kst83543216xuxxy8136.tmp of size 7196 and isopen=true finished after 0 time(s) through the loop

There's no way to run the real code inside a debugger to step through it, but from looking at the source to sun.nio.ch.FileChannelImpl.read() it looks like a 0 is returned if the file magically becomes closed while internal data structures are prepared; the code below is copied from the Java 7 reference implementation, comments added by me:

// sun.nio.ch.FileChannelImpl.java
public int read(ByteBuffer dst) throws IOException {
    ensureOpen();              // this throws if file is closed...
    if (!readable)
        throw new NonReadableChannelException();
    synchronized (positionLock) {
        int n = 0;
        int ti = -1;
        Object traceContext = IoTrace.fileReadBegin(path);
        try {
            begin();
            ti = threads.add();
            if (!isOpen())
                return 0;       // ...argh
            do {
                n = IOUtil.read(fd, dst, -1, nd);
            } while (......)
.......

But the debugging code tests isOpen() and gets true. So I don't know what's going wrong.

As the current test data files are tiny, I dropped this in place just to have something working:

works for now:

try {
    byte[] scratch = Files.readAllBytes(in);
    java.util.zip.CRC32 placeholder = new java.util.zip.CRC32();
    placeholder.update(scratch);
    // do stuff with placeholder.getValue()
}

I don't want to slurp the entire file into memory for the Real Code, because some of those files can be large. I do note that readAllBytes uses an InputStream in its reference implementation, which has no trouble reading the same file that SeekableByteChannel failed to. So I'll probably rewrite the code to just use input streams instead of byte channels. I'd still like to figure out what's gone wrong in case a future scenario comes up where we need to use byte channels. What am I missing with SeekableByteChannel?


Solution

  • Check that 'reasonable_buffer_size' isn't zero.