2727import java .lang .ref .PhantomReference ;
2828import java .lang .ref .Reference ;
2929import java .lang .ref .ReferenceQueue ;
30+ import java .util .Set ;
31+ import java .util .concurrent .ConcurrentHashMap ;
32+ import java .util .concurrent .TimeUnit ;
33+ import java .util .concurrent .atomic .AtomicBoolean ;
3034import java .util .logging .Level ;
3135import java .util .logging .Logger ;
3236
@@ -45,102 +49,65 @@ public static Cleaner getCleaner() {
4549 }
4650
4751 private final ReferenceQueue <Object > referenceQueue ;
48- private Thread cleanerThread ;
49- private CleanerRef firstCleanable ;
52+ private final Set < CleanerRef > uncleaned ;
53+ private final AtomicBoolean cleanerRunning ;
5054
5155 private Cleaner () {
5256 referenceQueue = new ReferenceQueue <>();
57+ uncleaned = ConcurrentHashMap .newKeySet ();
58+ cleanerRunning = new AtomicBoolean (false );
5359 }
5460
55- public synchronized Cleanable register (Object obj , Runnable cleanupTask ) {
61+ public Cleanable register (Object obj , Runnable cleanupTask ) {
5662 // The important side effect is the PhantomReference, that is yielded
5763 // after the referent is GCed
58- return add (new CleanerRef (this , obj , referenceQueue , cleanupTask ));
59- }
64+ Cleanable cleanable = add (new CleanerRef (obj , referenceQueue , cleanupTask ));
6065
61- private synchronized CleanerRef add (CleanerRef ref ) {
62- synchronized (referenceQueue ) {
63- if (firstCleanable == null ) {
64- firstCleanable = ref ;
65- } else {
66- ref .setNext (firstCleanable );
67- firstCleanable .setPrevious (ref );
68- firstCleanable = ref ;
69- }
70- if (cleanerThread == null ) {
71- Logger .getLogger (Cleaner .class .getName ()).log (Level .FINE , "Starting CleanerThread" );
72- cleanerThread = new CleanerThread ();
73- cleanerThread .start ();
74- }
75- return ref ;
66+ if (cleanerRunning .compareAndSet (false , true )) {
67+ Logger .getLogger (Cleaner .class .getName ()).log (Level .FINE , "Starting CleanerThread" );
68+ Thread cleanerThread = new CleanerThread ();
69+ cleanerThread .start ();
7670 }
71+
72+ return cleanable ;
7773 }
7874
79- private synchronized boolean remove (CleanerRef ref ) {
80- synchronized (referenceQueue ) {
81- boolean inChain = false ;
82- if (ref == firstCleanable ) {
83- firstCleanable = ref .getNext ();
84- inChain = true ;
85- }
86- if (ref .getPrevious () != null ) {
87- ref .getPrevious ().setNext (ref .getNext ());
88- }
89- if (ref .getNext () != null ) {
90- ref .getNext ().setPrevious (ref .getPrevious ());
91- }
92- if (ref .getPrevious () != null || ref .getNext () != null ) {
93- inChain = true ;
94- }
95- ref .setNext (null );
96- ref .setPrevious (null );
97- return inChain ;
98- }
75+ private CleanerRef add (final CleanerRef toAdd ) {
76+ uncleaned .add (toAdd );
77+ return toAdd ;
78+ }
79+
80+ // Remove by node reference
81+ private void remove (final CleanerRef node ) {
82+ uncleaned .remove (node );
9983 }
10084
10185 private static class CleanerRef extends PhantomReference <Object > implements Cleanable {
102- private final Cleaner cleaner ;
10386 private final Runnable cleanupTask ;
104- private CleanerRef previous ;
105- private CleanerRef next ;
87+ private final AtomicBoolean cleaned ;
10688
107- public CleanerRef (Cleaner cleaner , Object referent , ReferenceQueue <? super Object > q , Runnable cleanupTask ) {
89+ CleanerRef (Object referent , ReferenceQueue <? super Object > q , Runnable cleanupTask ) {
10890 super (referent , q );
109- this .cleaner = cleaner ;
11091 this .cleanupTask = cleanupTask ;
92+ this .cleaned = new AtomicBoolean (false );
11193 }
11294
11395 @ Override
11496 public void clean () {
115- if (cleaner .remove (this )) {
97+ if (cleaned .compareAndSet (false , true )) {
98+ INSTANCE .remove (this );
11699 cleanupTask .run ();
117100 }
118101 }
119-
120- CleanerRef getPrevious () {
121- return previous ;
122- }
123-
124- void setPrevious (CleanerRef previous ) {
125- this .previous = previous ;
126- }
127-
128- CleanerRef getNext () {
129- return next ;
130- }
131-
132- void setNext (CleanerRef next ) {
133- this .next = next ;
134- }
135102 }
136103
137- public static interface Cleanable {
138- public void clean ();
104+ public interface Cleanable {
105+ void clean ();
139106 }
140107
141108 private class CleanerThread extends Thread {
142109
143- private static final long CLEANER_LINGER_TIME = 30000 ;
110+ private final long CLEANER_LINGER_TIME = TimeUnit . SECONDS . toMillis ( 30L ) ;
144111
145112 public CleanerThread () {
146113 super ("JNA Cleaner" );
@@ -151,26 +118,23 @@ public CleanerThread() {
151118 public void run () {
152119 while (true ) {
153120 try {
154- Reference <? extends Object > ref = referenceQueue .remove (CLEANER_LINGER_TIME );
121+ Reference <?> ref = referenceQueue .remove (CLEANER_LINGER_TIME );
155122 if (ref instanceof CleanerRef ) {
156123 ((CleanerRef ) ref ).clean ();
157124 } else if (ref == null ) {
158- synchronized (referenceQueue ) {
159- Logger logger = Logger .getLogger (Cleaner .class .getName ());
160- if (firstCleanable == null ) {
161- cleanerThread = null ;
162- logger .log (Level .FINE , "Shutting down CleanerThread" );
163- break ;
164- } else if (logger .isLoggable (Level .FINER )) {
165- StringBuilder registeredCleaners = new StringBuilder ();
166- for (CleanerRef cleanerRef = firstCleanable ; cleanerRef != null ; cleanerRef = cleanerRef .next ) {
167- if (registeredCleaners .length () != 0 ) {
168- registeredCleaners .append (", " );
169- }
170- registeredCleaners .append (cleanerRef .cleanupTask .toString ());
125+ Logger logger = Logger .getLogger (Cleaner .class .getName ());
126+ if (cleanerRunning .compareAndSet (uncleaned .isEmpty (), false )) {
127+ logger .log (Level .FINE , "Shutting down CleanerThread" );
128+ break ;
129+ } else if (logger .isLoggable (Level .FINER )) {
130+ StringBuilder registeredCleaners = new StringBuilder ();
131+ uncleaned .forEach ((cleanerRef ) -> {
132+ if (registeredCleaners .length () != 0 ) {
133+ registeredCleaners .append (", " );
171134 }
172- logger .log (Level .FINER , "Registered Cleaners: {0}" , registeredCleaners .toString ());
173- }
135+ registeredCleaners .append (cleanerRef .cleanupTask .toString ());
136+ });
137+ logger .log (Level .FINER , "Registered Cleaners: {0}" , registeredCleaners .toString ());
174138 }
175139 }
176140 } catch (InterruptedException ex ) {
0 commit comments