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: [email protected]
For additional commands, e-mail: [email protected]