Description
Peter De Wachter opened SPR-10485 and commented
I'm experiencing deadlocks and other bugs when calling DeferredResult#setResult while that deferred result's timeout handler is running. This is on Tomcat 7.0.39. I'm using the following code to reproduce this.
@Controller
public class TestController {
private DeferredResult<String> deferred;
static class DoSomethingUseful implements Runnable {
public void run() {
try { Thread.sleep(2000); } catch (InterruptedException e) { }
}
}
@RequestMapping(value="/test/start")
@ResponseBody
public DeferredResult<String> start() {
deferred = new DeferredResult<String>(4000L, "timeout\n");
deferred.onTimeout(new DoSomethingUseful());
return deferred;
}
@RequestMapping(value="/test/stop")
@ResponseBody
public String stop() {
deferred.setResult("result\n");
return "ok\n";
}
}
The start method creates a DeferredResult with a 4 second timeout. To make the bug easy to reproduce, it has an onTimeout handler which will sleep for 2 seconds. The stop method will set a value for the DeferredResult. The code seems correct to me: the DeferredResult documentaion states that "the application can produce the result from a thread of its choice", and also that setResult can be called on an DeferredResult that's already expired.
To reproduce the bug, try something like this:
curl http://localhost/test/start & sleep 5; curl http://localhost/test/stop
On Tomcat using the APR connector, this will deadlock. On Tomcat using the NIO connector, this will sometimes (not consistently) never answer the start request and return the "timeout\n" string as a result of the stop request.
Affects: 3.2.2
Reference URL: spring-attic/spring-framework-issues#48
Issue Links:
- Concurrency problem in DeferredResult: potential double execution of handleResult [SPR-14978] #19544 Concurrency problem in DeferredResult: potential double execution of handleResult
- DeferredResult not thread-safe for isSetOrExpired call [SPR-13451] #18031 DeferredResult not thread-safe for isSetOrExpired call