Author: markt
Date: Tue Dec  2 20:16:01 2014
New Revision: 1643002

URL: http://svn.apache.org/r1643002
Log:
Fix concurrency issue identified while investigating BZ 57252. Ensure that 
non-container threads can't change the async state until the container thread 
has completed post-processing.

Modified:
    tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java

Modified: tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java?rev=1643002&r1=1643001&r2=1643002&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java [UTF-8] 
(original)
+++ tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java [UTF-8] Tue Dec  
2 20:16:01 2014
@@ -106,28 +106,30 @@ public class AsyncStateMachine {
         StringManager.getManager(Constants.Package);
 
     private static enum AsyncState {
-        DISPATCHED(false, false, false, false),
-        STARTING(true, true, false, false),
-        STARTED(true, true, false, false),
-        MUST_COMPLETE(true, true, true, false),
-        COMPLETING(true, false, true, false),
-        TIMING_OUT(true, false, false, false),
-        MUST_DISPATCH(true, true, false, true),
-        DISPATCHING(true, false, false, true),
-        READ_WRITE_OP(true, true, false, false),
-        ERROR(true, false, false, false);
+        DISPATCHED(false, false, false, false, false),
+        STARTING(true, true, false, false, true),
+        STARTED(true, true, false, false, false),
+        MUST_COMPLETE(true, true, true, false, false),
+        COMPLETING(true, false, true, false, false),
+        TIMING_OUT(true, false, false, false, false),
+        MUST_DISPATCH(true, true, false, true, true),
+        DISPATCHING(true, false, false, true, false),
+        READ_WRITE_OP(true, true, false, false, true),
+        ERROR(true, false, false, false, false);
 
         private final boolean isAsync;
         private final boolean isStarted;
         private final boolean isCompleting;
         private final boolean isDispatching;
+        private final boolean pauseNonContainerThread;
 
         private AsyncState(boolean isAsync, boolean isStarted, boolean 
isCompleting,
-                boolean isDispatching) {
+                boolean isDispatching, boolean pauseNonContainerThread) {
             this.isAsync = isAsync;
             this.isStarted = isStarted;
             this.isCompleting = isCompleting;
             this.isDispatching = isDispatching;
+            this.pauseNonContainerThread = pauseNonContainerThread;
         }
 
         public boolean isAsync() {
@@ -145,6 +147,10 @@ public class AsyncStateMachine {
         public boolean isCompleting() {
             return isCompleting;
         }
+
+        public boolean getPauseNonContainerThread() {
+            return pauseNonContainerThread;
+        }
     }
 
 
@@ -211,6 +217,12 @@ public class AsyncStateMachine {
      */
     public synchronized SocketState asyncPostProcess() {
 
+        // Unpause any non-container threads that may be waiting for this
+        // container thread to complete this method. Note because of the syncs
+        // those non-container threads won't start back up until until this
+        // method exits.
+        notifyAll();
+
         if (state == AsyncState.STARTING || state == AsyncState.READ_WRITE_OP) 
{
             state = AsyncState.STARTED;
             return SocketState.LONG;
@@ -241,8 +253,8 @@ public class AsyncStateMachine {
 
 
     public synchronized boolean asyncComplete() {
+        pauseNonContainerThread();
         boolean doComplete = false;
-
         if (state == AsyncState.STARTING) {
             state = AsyncState.MUST_COMPLETE;
         } else if (state == AsyncState.STARTED) {
@@ -282,6 +294,7 @@ public class AsyncStateMachine {
 
 
     public synchronized boolean asyncDispatch() {
+        pauseNonContainerThread();
         boolean doDispatch = false;
         if (state == AsyncState.STARTING) {
             state = AsyncState.MUST_DISPATCH;
@@ -377,4 +390,24 @@ public class AsyncStateMachine {
         processor.getRequest().listener = null;
         processor.getRequest().getResponse().listener = null;
     }
+
+
+    /*
+     * startAsync() has been called but the container thread where this was
+     * called has not completed processing. To avoid various race conditions -
+     * including several related to error page handling - pause this
+     * non-container thread until the container thread has finished processing.
+     * The non-container thread will be paused until the container thread
+     * completes asyncPostProcess().
+     */
+    private synchronized void pauseNonContainerThread() {
+        while (!ContainerThreadMarker.isContainerThread() &&
+                state.getPauseNonContainerThread()) {
+            try {
+                wait();
+            } catch (InterruptedException e) {
+                // TODO Log this?
+            }
+        }
+    }
 }



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

Reply via email to