I just found that following code doesn't work as expected.
try (AsynchronousFileChannel channel = open(path)) {
channel.read(, , ,
new CompletionHandler() {
@Override
public void completed(Integer result, Long attachment) {
}
@Override
public void failed(Throwable exc, Long attachment) {
// channel already has been closed!!!
}
});
}
Because the AsynchronousFileChannel#read method returns instantly and the CompletionHandler just can't work.
What is the common idiom for closing the channel, after tasks are done?
Should I just do this?
AsynchronousFileChannel channel = open(path);
channel.read(, , ,
new CompletionHandler() {
@Override
public void completed(Integer result, Long attachment) {
}
@Override
public void failed(Throwable exc, Long attachment) {
}
});
channel.close();
CodePudding user response:
Hopefully someone knows a proper way to ensure that all in-progress AsynchronousFileChannel callbacks are finished - I could not find a suitable API call to end a read operation with callbacks. In tests, I noticed that the CompletionHandler callbacks may be called after my method call returned data read to its caller, well after leaving the try with resources block below which closes the AsynchronousFileChannel:
try (AsynchronousFileChannel fc = AsynchronousFileChannel.open(p, StandardOpenOption.READ)) {
var handler = new CompletionHandler<Integer,Long>() {
public void completed(Integer result, Long position) {
// ...
}
public void failed(Throwable exc, Long position) {
// ...
}
};
// Run some reads:
fc.read(aByteBuffer, startPos, Long.valueOf(startPos), handler);
}
return blah;
// "handler" still can receive callbacks after this point!
Instead I used a workaround using a CountDownLatch inside the method so that the try-catch block did not exit until all the fc.read calls were matched with a corresponding callback to CompletionHandler completed / failed.
try (AsynchronousFileChannel fc = AsynchronousFileChannel.open(p, StandardOpenOption.READ)) {
// Work out how many fc.read calls you plan to do
int numberOfReads = 1;
CountDownLatch countdown = new CountDownLatch(numberOfReads);
var handler = new CompletionHandler<Integer,Long>() {
public void completed(Integer result, Long position) {
// ...
countdown.countDown();
}
public void failed(Throwable exc, Long position) {
// ...
countdown.countDown();
}
};
// Kick off numberOfReads x fc.read
fc.read(yourByteBuffer, startPos, Long.valueOf(startPos), handler);
// Without next line the AsynchronousFileChannel may continue to run after return
countdown.await();
}
return blah;
// No more "handler" callbacks after this point
It is not an ideal solution, as it relies on calculating the number of reads before creating the CountDownLatch. Instead you might consider a dynamic counter or use the non-callback read method: Future<Integer> fut = read(ByteBuffer, long) style and call fut.get(); in the caller thread instead.
