Avoid race while retiring executors

Today, a race condition exists when retiring executors. Namely, if an
executor is retired and then the thread pool is terminated, the retiring
of the executor and the termination of the thread pool can race to
remove the retired executor from the queue of retired executors. More
precisely, when the executor is initially retired, it is placed on a
queue of retired executors, and then removed when it is successfully
shutdown. When the pool is terminated, it will also drain the queue of
retired executors. This leads to a time-of-check-time-of-use race where
the draining can see a retired executor on the queue but that retired
executor can be removed upon successful shutdown of that executor. This
leads to the draining attempting to remove an element from the queue
when there is none. This commit addresses this race condition by instead
safely polling the queue.

Relates #18333
This commit is contained in:
Jason Tedor 2016-05-13 12:26:18 -04:00
parent d5b75491dc
commit 81898a2e3e
1 changed files with 10 additions and 4 deletions

View File

@ -440,8 +440,11 @@ public class ThreadPool extends AbstractComponent implements Closeable {
((ThreadPoolExecutor) executor.executor()).shutdownNow(); ((ThreadPoolExecutor) executor.executor()).shutdownNow();
} }
} }
while (!retiredExecutors.isEmpty()) {
((ThreadPoolExecutor) retiredExecutors.remove().executor()).shutdownNow(); ExecutorHolder holder;
while ((holder = retiredExecutors.poll()) != null) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) holder.executor();
executor.shutdownNow();
} }
} }
@ -452,10 +455,13 @@ public class ThreadPool extends AbstractComponent implements Closeable {
result &= ((ThreadPoolExecutor) executor.executor()).awaitTermination(timeout, unit); result &= ((ThreadPoolExecutor) executor.executor()).awaitTermination(timeout, unit);
} }
} }
while (!retiredExecutors.isEmpty()) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) retiredExecutors.remove().executor(); ExecutorHolder holder;
while ((holder = retiredExecutors.poll()) != null) {
ThreadPoolExecutor executor = (ThreadPoolExecutor) holder.executor();
result &= executor.awaitTermination(timeout, unit); result &= executor.awaitTermination(timeout, unit);
} }
estimatedTimeThread.join(unit.toMillis(timeout)); estimatedTimeThread.join(unit.toMillis(timeout));
return result; return result;
} }