Is the following code thread-safe? If so, what guarantees the safe publication of the ByteBuffer
instance to the thread executing the CompletionHandler
?
AsynchronousSocketChannel channel = ...
ByteBuffer buf = ByteBuffer.allocate(1024);
channel.read(buf, null, new CompletionHandler<Integer, Void>() {
//"completed" can be executed by a different thread than channel.read()
public void completed(Integer result, Void attachment) {
buf.flip(); //Can buf be safely accessed here? If so, why?
//...
}
public void failed(Throwable exc, Void attachment) {
//...
}
});
an authoritative reference that is valid for all JVMs and all platforms
The only such authoritative source I know of is javadocs (1, 2, 3).
Unfortunately, as you can see for yourself, they contain no explicit and clear guarantees of thread-safety.
It means the code is not thread-safe.
IMO the guarantees should be given in the javadoc for the method, the method's class, or CompletionHandler
— then we can be sure they are implemented for all JVMs and all platforms (and will stay implemented in the future).
But if you really want, you can "compile" a proof for thread-safety from multiple places in different javadocs:
AsynchronousSocketChannel.read(...)
:
The handler parameter is a completion handler that is invoked when the read operation completes (or fails).
java.nio.channels
:
Asynchronous channels are bound to an asynchronous channel group for the purpose of resource sharing. A group has an associated ExecutorService to which tasks are submitted to handle I/O events and dispatch to completion handlers that consume the result of asynchronous operations performed on channels in the group.
ExecutorService
:
Memory consistency effects: Actions in a thread prior to the submission of a Runnable or Callable task to an ExecutorService happen-before any actions taken by that task
As a result, we get that every action of the I/O read to ByteBuffer
happens-before the first action of CompletionHandler
=> this means the code is thread-safe.
IMO "compiled proofs" like the one above are too fragile, and personally I would assume that the code is not thread-safe.
I don't see in javadocs any explicit guarantees about inner state of a ByteBuffer
, used in a read()
operation, to be visible inside CompletionHandler
called when the read()
completes.
So there is no 100% guarantee.
But I would say that it is common sense to expect that to be true. Simply because:
CompletionHandler
to see the changes made by the 'read()'CompletionHandler
can be run asynchronously in a different thread — is a detail of read()
's internal implementation. As a result, it's a read()
's concern to make sure that thing are safely published in such cases.But if you are unsure/unconvinced, and you don't develop applications with nanosecond delays — just add your own additional synchronization — and you will be 100% sure that your program works correctly.
Just to add to the answer above, you might prefer to pass ByteBuffer
to CompletionHandler
via attachment
argument of read(...)
method (as it's done in various examples):
AsynchronousSocketChannel channel = ...
ByteBuffer buf = ByteBuffer.allocate(1024);
channel.read(buf, buf, new CompletionHandler<>() {
public void completed(Integer result, ByteBuffer buf) {
buf.flip();
//...
}
public void failed(Throwable exc, ByteBuffer buf) {
//...
}
});
Since attachment
is an explicit argument for read(...)
, it must be safely published to CompletionHandler
's thread.
Unfortunately, I don't see in javadocs any explicit guarantees that this safe publication occurs after the read(...)
completes.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With