2929 */
3030package org .jruby .embed .internal ;
3131
32- import java .lang .ref .Cleaner ;
33- import java .lang .ref .Cleaner .Cleanable ;
32+ import java .lang .ref .PhantomReference ;
33+ import java .lang .ref .Reference ;
34+ import java .lang .ref .ReferenceQueue ;
3435import java .util .concurrent .ConcurrentHashMap ;
3536import java .util .concurrent .atomic .AtomicReference ;
3637import java .util .function .Supplier ;
4344 */
4445class ThreadLocalContext {
4546 private final ConcurrentHashMap <LocalContextCleaningAction , Object > contextRefs = new ConcurrentHashMap <>();
46- private final Cleaner cleaner = Cleaner .create ();
4747 private final Supplier <LocalContext > localContextFactory ;
48+ private final Cleaner cleaner = new Cleaner ();
4849
4950 public ThreadLocalContext (final Supplier <LocalContext > localContextFactory ) {
5051 this .localContextFactory = localContextFactory ;
@@ -61,13 +62,13 @@ protected AtomicReference<LocalContextCleaningAction> initialValue() {
6162 // GC'd (i.e. when this class gets GC'd because terminate() was never called).
6263 // see ThreadLocal JavaDoc: ref will stay reachable "as long as the thread is
6364 // alive and the ThreadLocal instance is accessible"
64- final Cleanable cleanable = cleaner .register (ref , ctx );
65+ ctx .register (ref , cleaner );
6566 if (contextHolder == null )
6667 // boundary case if we're already terminating: clean up immediately, because
6768 // there is no more cleanup thread to do that later
6869 // the returned context will be null, but that's to be expected when operating
6970 // on an object that has been terminated
70- cleanable . clean ();
71+ ctx . run ();
7172 return ref ;
7273 }
7374 };
@@ -77,6 +78,7 @@ public LocalContext get() {
7778 }
7879
7980 public void terminate () {
81+ cleaner .interrupt ();
8082 contextHolder = null ;
8183 for (final LocalContextCleaningAction ref : contextRefs .keySet ())
8284 ref .run ();
@@ -88,11 +90,14 @@ public void terminate() {
8890 * everything that is GC-reachable from them will stay reachable until the
8991 * cleaning action has been run.
9092 */
91- private static class LocalContextCleaningAction extends AtomicReference <LocalContext > implements Runnable {
93+ static class LocalContextCleaningAction extends AtomicReference <LocalContext > implements Runnable {
9294 private static final long serialVersionUID = 1L ;
9395
9496 private final ConcurrentHashMap <LocalContextCleaningAction , Object > contextRefs ;
9597
98+ @ SuppressWarnings ("unused" ) // only used to make sure the PhantomReference doesn't get GC'd
99+ private CleanerReference phantomReference ;
100+
96101 private LocalContextCleaningAction (final ConcurrentHashMap <LocalContextCleaningAction , Object > contextRefs ,
97102 final LocalContext context ) {
98103 super (context );
@@ -111,5 +116,42 @@ public void run() {
111116 lc .remove ();
112117 contextRefs .remove (this );
113118 }
119+
120+ private void register (final AtomicReference <LocalContextCleaningAction > ref , final Cleaner cleaner ) {
121+ phantomReference = new CleanerReference (ref , cleaner .q , this );
122+ }
123+ }
124+
125+ private static class CleanerReference extends PhantomReference <AtomicReference <LocalContextCleaningAction >> {
126+ private Runnable cleanup ;
127+
128+ public CleanerReference (final AtomicReference <LocalContextCleaningAction > referent ,
129+ final ReferenceQueue <AtomicReference <LocalContextCleaningAction >> q , final Runnable cleanup ) {
130+ super (referent , q );
131+ this .cleanup = cleanup ;
132+ }
133+ }
134+
135+ private static class Cleaner extends Thread {
136+ private final ReferenceQueue <AtomicReference <LocalContextCleaningAction >> q = new ReferenceQueue <>();
137+
138+ public Cleaner () {
139+ setName ("JRuby-ThreadLocalContext-Cleaner-" + getId ());
140+ setDaemon (true );
141+ start ();
142+ }
143+
144+ @ Override
145+ public void run () {
146+ while (!interrupted ()) {
147+ final Reference <?> cleanable ;
148+ try {
149+ cleanable = q .remove ();
150+ } catch (InterruptedException e ) {
151+ break ;
152+ }
153+ ((CleanerReference ) cleanable ).cleanup .run ();
154+ }
155+ }
114156 }
115157}
0 commit comments