SimpleHttpServer doesn't shut down cleanly on some platforms
------------------------------------------------------------
Key: WSCOMMONS-402
URL: https://issues.apache.org/jira/browse/WSCOMMONS-402
Project: WS-Commons
Issue Type: Bug
Environment: minotaur.apache.org: Java HotSpot(TM) 64-Bit Server VM
(build diablo-1.5.0_07-b01, mixed mode)
Reporter: Andreas Veithen
Priority: Minor
Different JVM implementations show different behaviors when Thread#interrupt is
called on a thread that is blocked in ServerSocket#accept:
1. Most JVMs only set the interrupted flag on the thread but don't trigger an
exception.
2. Other JVMs cause a java.io.InterruptedIOException to be thrown out of
ServerSocket#accept but don't set the interrupted flag.
On JVMs that have behavior 1, when calling SimpleHttpServer#destroy, the
sequence of events is as follows:
1. SimpleHttpServer#destroy calls ExecutorServide#shutdownNow.
2. ExecutorServide#shutdownNow calls Thread#interrupted on the thread running
DefaultConnectionListener (and blocked in ServerSocket#accept). This sets the
interrupted flag, but doesn't cause an exception.
3. SimpleHttpServer#destroy calls ExecutorServide#awaitTermination which will
only return after the specified timeout (because the DefaultConnectionListener
thread is still running).
4. SimpleHttpServer#destroy calls DefaultConnectionListener#destroy which
closes the ServerSocket.
5. ServerSocket#accept throws an exception. The catch block in
DefaultConnectionListener#run checks the interrupted flag and since it is set,
the method exits, terminating the thread.
Except for step 3, this approach is correct and results in a clean shutdown.
On JVMs having the other behavior, the sequence of events is completely
different:
1. SimpleHttpServer#destroy calls ExecutorServide#shutdownNow.
2. ExecutorServide#shutdownNow calls Thread#interrupted on the thread running
DefaultConnectionListener (and blocked in ServerSocket#accept).
3. An InterruptedIOException is thrown out of ServerSocket#accept. Since the
interrupted flag is not set, the exception is handed over to the
DefaultConnectionListenerFailureHandler.
4. DefaultConnectionListenerFailureHandler#failed returns true and
DefaultConnectionListener will immediately reenter ServerSocket#accept.
5. SimpleHttpServer#destroy calls ExecutorServide#awaitTermination which will
only return after the specified timeout (because the DefaultConnectionListener
thread is still running).
6. SimpleHttpServer#destroy calls DefaultConnectionListener#destroy which
closes the ServerSocket.
7. ServerSocket#accept throws an exception. Since the interrupted flag is still
not set, the exception is again handed over to
DefaultConnectionListenerFailureHandler#failed which instructs the
DefaultConnectionListener to retry.
8. Since the original server socket is now closed,
DefaultConnectionListener#run will create a new ServerSocket and open the port
again. It then blocks in ServerSocket#accept.
9. SimpleHttpServer#destroy shuts down the request processors and exits.
As can be seen this leaves the HTTP port open and SimpleHttpServer doesn't shut
down cleanly.
The problem can be reproduced by running the transport tests on minotaur
(peope.apache.org). The HTTP tests will fail with an "Address already in use"
error because at some point a test case tries to start a different HTTP server
on the port that is still used by the SimpleHttpServer instance that didn't
shut down cleanly.
--
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.