Skip to content

Commit 6fce782

Browse files
Merge #75 - Terminate ArrayBuilder threadpool
(very) surprisingly, a static thread pool executor will prevent the JVM from ever properly shutting down, because of a circular nature of the references. Static variables and thread objects are GC roots, and the thread pool object references its child threads while the threads themselves reference the threadpool. No amount of GC runs will make anything unreachable, so the JVM can't finalize and shut down. To break the cycle, we need to forcibly shutdown the thread pool, which causes it to kill its threads and allows the GC to unravel the references. see also: https://stackoverflow.com/a/10395700
2 parents ba581f0 + 0c16bfb commit 6fce782

File tree

1 file changed

+27
-5
lines changed
  • src/main/java/edu/vanderbilt/accre/laurelin

1 file changed

+27
-5
lines changed

src/main/java/edu/vanderbilt/accre/laurelin/Root.java

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,15 +110,37 @@ static class TTreeDataSourceV2PartitionReader implements InputPartitionReader<Co
110110
private Map<String, SlimTBranch> slimBranches;
111111

112112
/**
113-
* Thread factory for async processing/decompression of baskets
113+
* ThreadPool handling the async decompression tasks
114114
*/
115-
private static ThreadFactory namedThreadFactory =
116-
new ThreadFactoryBuilder().setNameFormat("laurelin-arraybuilder-%d").build();
115+
private static ThreadPoolExecutor staticExecutor;
117116

118117
/**
119-
* ThreadPool handling the async decompression tasks
118+
* (very) surprisingly, a static thread pool executor will prevent the
119+
* JVM from ever properly shutting down, because of a circular nature
120+
* of the references. Static variables and thread objects are GC roots,
121+
* and the thread pool object references its child threads while the
122+
* threads themselves reference the threadpool. No amount of GC runs
123+
* will make anything unreachable, so the JVM can't finalize and
124+
* shut down. To break the cycle, we need to forcibly shutdown the
125+
* thread pool, which causes it to kill its threads and allows the GC
126+
* to unravel the references.
127+
* <p>
128+
* see also: https://stackoverflow.com/a/10395700
120129
*/
121-
private static ThreadPoolExecutor staticExecutor = new ThreadPoolExecutor(1,1,60L, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
130+
static {
131+
ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("laurelin-arraybuilder-%d").build();
132+
staticExecutor = new ThreadPoolExecutor(1, 1,
133+
5L, TimeUnit.SECONDS,
134+
new LinkedBlockingQueue<Runnable>(),
135+
factory);
136+
staticExecutor.allowCoreThreadTimeOut(true);
137+
Runtime.getRuntime().addShutdownHook(new Thread() {
138+
@Override
139+
public void run() {
140+
TTreeDataSourceV2PartitionReader.staticExecutor.shutdownNow();
141+
}
142+
});
143+
}
122144

123145
/**
124146
* Holds the async threadpool if enabled, null otherwise

0 commit comments

Comments
 (0)