Author: markt
Date: Wed Feb 11 14:22:00 2015
New Revision: 1658966
URL: http://svn.apache.org/r1658966
Log:
Follow up to r1594198
Limit cases where a dispatch is not performed because processing is already on
a poller thread.
Modified:
tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java
tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.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=1658966&r1=1658965&r2=1658966&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java [UTF-8]
(original)
+++ tomcat/trunk/java/org/apache/coyote/AsyncStateMachine.java [UTF-8] Wed Feb
11 14:22:00 2015
@@ -299,10 +299,22 @@ public class AsyncStateMachine {
if (state == AsyncState.STARTING) {
state = AsyncState.MUST_DISPATCH;
} else if (state == AsyncState.STARTED ||
- state == AsyncState.READ_WRITE_OP ||
state == AsyncState.TIMING_OUT ||
state == AsyncState.ERROR) {
state = AsyncState.DISPATCHING;
+ // A dispatch is always required.
+ // If on a non-container thread, need to get back onto a container
+ // thread to complete the processing.
+ // If on a container thread the current request/response are not
the
+ // request/response associated with the AsyncContext so need a new
+ // container thread to process the different request/response.
+ doDispatch = true;
+ } else if (state == AsyncState.READ_WRITE_OP) {
+ state = AsyncState.DISPATCHING;
+ // If on a container thread then the socket will be added to the
+ // poller poller when the thread exits the
+ // AbstractConnectionHandler.process() method so don't do a
dispatch
+ // here which would add it to the poller a second time.
if (!ContainerThreadMarker.isContainerThread()) {
doDispatch = true;
}
Modified: tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java?rev=1658966&r1=1658965&r2=1658966&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java
(original)
+++ tomcat/trunk/test/org/apache/catalina/core/TestAsyncContextImpl.java Wed
Feb 11 14:22:00 2015
@@ -2186,4 +2186,106 @@ public class TestAsyncContextImpl extend
}
}
}
+
+ @Test
+ public void testDispatchFromOtherContainerThread() throws Exception {
+ resetTracker();
+ // Setup Tomcat instance
+ Tomcat tomcat = getTomcatInstance();
+
+ // No file system docBase required
+ Context ctx = tomcat.addContext("", null);
+
+ NonAsyncServlet nonAsyncServlet = new NonAsyncServlet();
+ Tomcat.addServlet(ctx, "nonAsyncServlet", nonAsyncServlet);
+ ctx.addServletMapping("/target", "nonAsyncServlet");
+
+ AsyncStashServlet asyncStashServlet = new AsyncStashServlet();
+ Wrapper w1 = Tomcat.addServlet(ctx, "asyncStashServlet",
asyncStashServlet);
+ w1.setAsyncSupported(true);
+ ctx.addServletMapping("/asyncStashServlet", "asyncStashServlet");
+
+ AsyncRetrieveServlet asyncRetrieveServlet = new AsyncRetrieveServlet();
+ Wrapper w2 = Tomcat.addServlet(ctx, "asyncRetrieveServlet",
asyncRetrieveServlet);
+ w2.setAsyncSupported(true);
+ ctx.addServletMapping("/asyncRetrieveServlet", "asyncRetrieveServlet");
+
+ tomcat.start();
+
+ // First request in separate thread because the response won't be
+ // written until after the second request has been made.
+ Thread t = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ getUrl("http://localhost:" + getPort() +
"/asyncStashServlet");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ t.start();
+
+ // Wait for first request to get as far as it can
+ int count = 0;
+ while (count < 100 && getTrack() != null &&
+ !getTrack().startsWith("AsyncStashServletGet-")) {
+ count++;
+ Thread.sleep(100);
+ }
+
+ getUrl("http://localhost:" + getPort() + "/asyncRetrieveServlet");
+
+ // Wait for second request to release first and allow it to complete
+ String expectedTrack =
"AsyncStashServletGet-AsyncRetrieveServletGet-NonAsyncServletGet-";
+ count = 0;
+ while (count < 100 && !getTrack().equals(expectedTrack)) {
+ count++;
+ Thread.sleep(100);
+ }
+
+ Assert.assertEquals(expectedTrack, getTrack());
+ }
+
+ private static class AsyncStashServlet extends HttpServlet {
+
+ private static final long serialVersionUID = 1L;
+ private static final String DEFAULT_KEY = "DEFAULT";
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ String key = req.getParameter("key");
+ if (key == null) {
+ key = DEFAULT_KEY;
+ }
+
+ req.getServletContext().setAttribute(key, req.startAsync());
+ TestAsyncContextImpl.track("AsyncStashServletGet-");
+ }
+ }
+
+ private static class AsyncRetrieveServlet extends HttpServlet {
+ private static final long serialVersionUID = 1L;
+ private static final String DEFAULT_KEY = "DEFAULT";
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ String key = req.getParameter("key");
+ if (key == null) {
+ key = DEFAULT_KEY;
+ }
+
+ AsyncContext ac = (AsyncContext)
req.getServletContext().getAttribute(key);
+ if (ac == null) {
+ TestAsyncContextImpl.track("FAIL:nullAsyncContext-");
+ } else {
+ TestAsyncContextImpl.track("AsyncRetrieveServletGet-");
+ ac.dispatch("/target");
+ }
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]