-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Use a non-blocking output stream for socket writes #1769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Sorry for the checkstyle issues, tested this as an anonymus class before moving to an inner class and cleaning it up properly :/ |
@derklaro no worries, and thanks for spotting the issue and providing a fix! Do you think this is something testable? Would really love to have it covered, to avoid regressions in the future in this critical piece of code π |
I'm not sure if this can be tested actually as it's requiring java 16+ to run while all builds can only run on java 8 π€ |
@derklaro ah, I didn't notice that this code is only for |
I was wrong there. They resolved other issues by replacing it with locks but not the synchronize issue. The point there is that other channels might not allow concurrent reading and writing which may lead to problems... |
To bring everything in order again as my comment is a bit messed up. JDK-4774871 (Channels.newInputStream() and newOutputStream() synchronize too much) describes the issue I was oberserving with the channel input and output stream methods previously. JDK-4509080 (Streams inhibit concurrent reading & writing) is explecitly talking about the Socket#write and Socket#read methods which are blocking as well, comment on that issue: "The socket adaptors were partly re-implemented as part of JDK-8222774 and so are no longer constrained to use the blockingLock. As a result, concurrent reading and writing with the socket adpator is possible", which does not mean that Channels.newInputStream and Channels.newOutputStream are no longer blocking as they will still block on the lock provided by SelectableByteChannel. To sum it up: Socket#write and Socket#read are no longer blocking while the Channels.new Input/Output Stream methods are still using the blocking lock (which is an expected behaviour). So there are 2 possibilities here: Either we re-implement our own input and output stream, or (what i did) implement one of them on our own. (OutputStream is the better choice imo as it isn't as much code as the InputStream) to prevent the concurrent synchronize on the blocking lock. Hope this explains everything better now! :) |
Hey, are there any updates on this? I'm facing the same issue π’ |
Hi @derklaro, I finally got a chance to dive deeper into this issue. Do I understand correctly that the actual problem is in If so, perhaps a much simpler solution would be to make passed |
Hey, You mean by creating a special implementation of WriteableByteChannel which just passes all method calls down to the actual channel? Thats sound easier to me as well π |
@derklaro yep, that's the idea :) To reuse as much JDK's code as possible. Something like: class PlainWritableByteChannel implements WritableByteChannel {
@Override
public int write(ByteBuffer src) throws IOException {
return socketChannel.write(src);
}
@Override
public boolean isOpen() {
return socketChannel.isOpen();
}
@Override
public void close() throws IOException {
socketChannel.close();
}
}
return Channels.newOutputStream(new PlainWritableByteChannel()); |
Also... any chance we can unit test this? |
The only way i see is to write a test which only runs when we test on Java 16+ or above, which kinda breaks the purpose for a test... |
@derklaro okay, I tried playing with implementing it with JDK8 only classes but the test becomes a bit too involved. Also,
Let me double check the implications of removing the synchronization with the JDK community because it must be there for a reason, and if it's okay for us to not have it, I will be happy to finally merge this π |
Sounds good to me π |
FTR I just added Java 17 to the CI matrix. In fact, this PR should fix it because currently the main branch is broken and fails on that "attach" test :D |
Okay... after not hearing any concerns from anyone, I am going to YOLO it π I can confirm that it fixed the test, and other tests are passing as well, so I see no reasons why we shouldn't be doing this. |
Essentially:
Channels.newOutputStream
andChannels.newInputStream
are synchronizing on the blocking lock provided by theSelectableChannel#blockingLock
method. This prevents writing a command to a container stdin while there is no output printed to stdout. This is a long known issue in java and was fixed partially in java 13 (see https://bugs.java.com/bugdatabase/view_bug.do?bug_id=8222774). Concurrent read/writes aren't a problem there anymore as well.The OutputStream implementation used is adapted from the one returned by
Channels.newOutputStream
and moves the code ofChannels.writeFullyImpl
directly into the write method.References:
Closes #1768