This is an automated email from the ASF dual-hosted git repository.

markt pushed a commit to branch 9.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/9.0.x by this push:
     new 879d81be35 Refactor to remove code paths that could result in lost 
notifications
879d81be35 is described below

commit 879d81be35fdbe3e27fbf726a049a1827c827ab8
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 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                       |  9 +++
 2 files changed, 47 insertions(+), 39 deletions(-)

diff --git a/java/org/apache/tomcat/util/net/NioEndpoint.java 
b/java/org/apache/tomcat/util/net/NioEndpoint.java
index 915a057297..e7491ea3f9 100644
--- a/java/org/apache/tomcat/util/net/NioEndpoint.java
+++ b/java/org/apache/tomcat/util/net/NioEndpoint.java
@@ -1317,26 +1317,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
                             }
                         }
                     }
@@ -1428,33 +1427,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 f897437162..cbc85b9147 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -117,6 +117,15 @@
       </fix>
     </changelog>
   </subsection>
+  <subsection name="Coyote">
+    <changelog>
+      <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>
 </section>
 <section name="Tomcat 9.0.78 (remm)" rtext="release in progress">
   <subsection name="Other">


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to