This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/10.1.x by this push: new b4d81f0599 Refactor to remove code paths that could result in lost notifications b4d81f0599 is described below commit b4d81f05996a4d1a0333e2776f8c0cf1b0204c6b Author: Mark Thomas <ma...@apache.org> AuthorDate: Thu Jul 6 13:25:14 2023 +0100 Refactor to remove code paths that could result in lost notifications The scenario is considered unlikely but if, in the original code, the Poller notified the [read|write]Lock after the read/write registration but before execution entered the synchronized block, that notification would be lost. This could lead to a timeout rather than the expected read/write. Flagged by Coverity Scan for issues with spurious wake-ups. That wasn't an issue but this was. --- java/org/apache/tomcat/util/net/NioEndpoint.java | 77 ++++++++++++------------ webapps/docs/changelog.xml | 5 ++ 2 files changed, 43 insertions(+), 39 deletions(-) diff --git a/java/org/apache/tomcat/util/net/NioEndpoint.java b/java/org/apache/tomcat/util/net/NioEndpoint.java index 5f9beb80c8..e817945272 100644 --- a/java/org/apache/tomcat/util/net/NioEndpoint.java +++ b/java/org/apache/tomcat/util/net/NioEndpoint.java @@ -1267,26 +1267,25 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel,SocketChannel> throw new SocketTimeoutException(); } } - n = getSocket().read(buffer); - if (n == -1) { - throw new EOFException(); - } else if (n == 0) { - if (!readBlocking) { - readBlocking = true; - registerReadInterest(); - } - synchronized (readLock) { - if (readBlocking) { - try { - if (timeout > 0) { - startNanos = System.nanoTime(); - readLock.wait(timeout); - } else { - readLock.wait(); - } - } catch (InterruptedException e) { - // Continue + synchronized (readLock) { + n = getSocket().read(buffer); + if (n == -1) { + throw new EOFException(); + } else if (n == 0) { + // Ensure a spurious wake-up doesn't trigger a duplicate registration + if (!readBlocking) { + readBlocking = true; + registerReadInterest(); + } + try { + if (timeout > 0) { + startNanos = System.nanoTime(); + readLock.wait(timeout); + } else { + readLock.wait(); } + } catch (InterruptedException e) { + // Continue } } } @@ -1378,33 +1377,33 @@ public class NioEndpoint extends AbstractJsseEndpoint<NioChannel,SocketChannel> throw previousIOException; } } - n = getSocket().write(buffer); - if (n == 0 && (buffer.hasRemaining() || getSocket().getOutboundRemaining() > 0)) { + synchronized (writeLock) { + n = getSocket().write(buffer); // n == 0 could be an incomplete write but it could also // indicate that a previous incomplete write of the // outbound buffer (for TLS) has now completed. Only // block if there is still data to write. - writeBlocking = true; - registerWriteInterest(); - synchronized (writeLock) { - if (writeBlocking) { - try { - if (timeout > 0) { - startNanos = System.nanoTime(); - writeLock.wait(timeout); - } else { - writeLock.wait(); - } - } catch (InterruptedException e) { - // Continue + if (n == 0 && (buffer.hasRemaining() || getSocket().getOutboundRemaining() > 0)) { + // Ensure a spurious wake-up doesn't trigger a duplicate registration + if (!writeBlocking) { + writeBlocking = true; + registerWriteInterest(); + } + try { + if (timeout > 0) { + startNanos = System.nanoTime(); + writeLock.wait(timeout); + } else { + writeLock.wait(); } - writeBlocking = false; + } catch (InterruptedException e) { + // Continue } + } else if (startNanos > 0) { + // If something was written, reset timeout + timeout = getWriteTimeout(); + startNanos = 0; } - } else if (startNanos > 0) { - // If something was written, reset timeout - timeout = getWriteTimeout(); - startNanos = 0; } } while (buffer.hasRemaining() || getSocket().getOutboundRemaining() > 0); } else { diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index dd78d44509..a25727b227 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -154,6 +154,11 @@ based keys/certificates and logs the relevant information for each. (markt) </fix> + <fix> + Refactor blocking reads and writes for the NIO connector to remove + code paths that could allow a notification from the Poller to be missed + resuting in a timeout rather than the expected read or write. (markt) + </fix> </changelog> </subsection> <subsection name="WebSocket"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org