Skip to content

Commit 81dc0cc

Browse files
committed
Eliminate race condition in Tomcat's graceful shutdown
There was a race condition between the thread that's waiting for Tomcat to become inactive or the graceful shutdown to be aborted and the thread that aborts the shutdown and stops Tomcat when the grace period has elapsed. This race can lead to Tomcat appearing to have become inactive before the abort of the shutdown is noticed. When this happens, the result of the shutdown is reported as IDLE when it should have been REQUESTS_ACTIVE. The consequences of this are mostly benign although it does affect the log messages that are emitted. It is also causing some of our graceful shutdown tests to be flaky. This commit eliminates the race condition by considering the state of the aborted flag before logging and returning the result of the shutdown. Closes gh-39942
1 parent c83f701 commit 81dc0cc

File tree

1 file changed

+23
-18
lines changed
  • spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat

1 file changed

+23
-18
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/GracefulShutdown.java

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -67,26 +67,15 @@ private void doShutdown(GracefulShutdownCallback callback, CountDownLatch shutdo
6767
List<Connector> connectors = getConnectors();
6868
connectors.forEach(this::close);
6969
shutdownUnderway.countDown();
70-
try {
71-
for (Container host : this.tomcat.getEngine().findChildren()) {
72-
for (Container context : host.findChildren()) {
73-
while (isActive(context)) {
74-
if (this.aborted) {
75-
logger.info("Graceful shutdown aborted with one or more requests still active");
76-
callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
77-
return;
78-
}
79-
Thread.sleep(50);
80-
}
81-
}
82-
}
83-
70+
awaitInactiveOrAborted();
71+
if (this.aborted) {
72+
logger.info("Graceful shutdown aborted with one or more requests still active");
73+
callback.shutdownComplete(GracefulShutdownResult.REQUESTS_ACTIVE);
8474
}
85-
catch (InterruptedException ex) {
86-
Thread.currentThread().interrupt();
75+
else {
76+
logger.info("Graceful shutdown complete");
77+
callback.shutdownComplete(GracefulShutdownResult.IDLE);
8778
}
88-
logger.info("Graceful shutdown complete");
89-
callback.shutdownComplete(GracefulShutdownResult.IDLE);
9079
}
9180
finally {
9281
shutdownUnderway.countDown();
@@ -106,6 +95,22 @@ private void close(Connector connector) {
10695
connector.getProtocolHandler().closeServerSocketGraceful();
10796
}
10897

98+
private void awaitInactiveOrAborted() {
99+
try {
100+
for (Container host : this.tomcat.getEngine().findChildren()) {
101+
for (Container context : host.findChildren()) {
102+
while (!this.aborted && isActive(context)) {
103+
Thread.sleep(50);
104+
}
105+
}
106+
}
107+
108+
}
109+
catch (InterruptedException ex) {
110+
Thread.currentThread().interrupt();
111+
}
112+
}
113+
109114
private boolean isActive(Container context) {
110115
try {
111116
if (((StandardContext) context).getInProgressAsyncCount() > 0) {

0 commit comments

Comments
 (0)