2727import java .lang .ref .PhantomReference ;
2828import java .lang .ref .Reference ;
2929import java .lang .ref .ReferenceQueue ;
30+ import java .util .concurrent .TimeUnit ;
3031import java .util .logging .Level ;
3132import java .util .logging .Logger ;
3233
3536 * objects. It replaces the {@code Object#finalize} based resource deallocation
3637 * that is deprecated for removal from the JDK.
3738 *
38- * <p><strong>This class is intented to be used only be JNA itself.</strong></p>
39+ * <p><strong>This class is intended to be used only be JNA itself.</strong></p>
3940 */
4041public class Cleaner {
4142 private static final Cleaner INSTANCE = new Cleaner ();
43+ private static final Logger logger = Logger .getLogger (Cleaner .class .getName ());
44+ private static final long CLEANER_LINGER_TIME = TimeUnit .SECONDS .toMillis (30 );
4245
4346 public static Cleaner getCleaner () {
4447 return INSTANCE ;
4548 }
4649
4750 private final ReferenceQueue <Object > referenceQueue ;
48- private Thread cleanerThread ;
51+ private boolean cleanerRunning ;
4952 private CleanerRef firstCleanable ;
5053
5154 private Cleaner () {
5255 referenceQueue = new ReferenceQueue <>();
5356 }
5457
55- public synchronized Cleanable register (Object obj , Runnable cleanupTask ) {
56- // The important side effect is the PhantomReference, that is yielded
57- // after the referent is GCed
58- return add (new CleanerRef (this , obj , referenceQueue , cleanupTask ));
59- }
58+ public synchronized Cleanable register (final Object obj , final Runnable cleanupTask ) {
59+ // The important side effect is the PhantomReference, that is yielded after the referent is GCed
60+ final CleanerRef ref = new CleanerRef (obj , referenceQueue , cleanupTask );
6061
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 ;
62+ if (firstCleanable != null ) {
63+ ref .setNext (firstCleanable );
64+ firstCleanable .setPrevious (ref );
65+ }
66+ firstCleanable = ref ;
67+
68+ if (!cleanerRunning ) {
69+ logger .log (Level .FINE , "Starting CleanerThread" );
70+ Thread cleanerThread = new CleanerThread ();
71+ cleanerThread .start ();
72+ cleanerRunning = true ;
7673 }
74+
75+ return ref ;
7776 }
7877
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 ;
78+ private synchronized boolean remove (final CleanerRef ref ) {
79+ final CleanerRef prev = ref .getPrevious ();
80+ final CleanerRef next = ref .getNext ();
81+ boolean inChain = false ;
82+
83+ if (ref == firstCleanable ) {
84+ firstCleanable = next ;
85+ inChain = true ;
86+ }
87+ if (prev != null ) {
88+ prev .setNext (next );
89+ inChain = true ;
9890 }
91+ if (next != null ) {
92+ next .setPrevious (prev );
93+ inChain = true ;
94+ }
95+ return inChain ;
9996 }
10097
10198 private static class CleanerRef extends PhantomReference <Object > implements Cleanable {
102- private final Cleaner cleaner ;
10399 private final Runnable cleanupTask ;
104100 private CleanerRef previous ;
105101 private CleanerRef next ;
106102
107- public CleanerRef (Cleaner cleaner , Object referent , ReferenceQueue <? super Object > q , Runnable cleanupTask ) {
108- super (referent , q );
109- this .cleaner = cleaner ;
103+ public CleanerRef (final Object referent , final ReferenceQueue <? super Object > queue , final Runnable cleanupTask ) {
104+ super (referent , queue );
110105 this .cleanupTask = cleanupTask ;
111106 }
112107
113108 @ Override
114109 public void clean () {
115- if (cleaner .remove (this )) {
110+ if (INSTANCE .remove (this )) {
111+ previous = null ;
112+ next = null ;
116113 cleanupTask .run ();
117114 }
118115 }
@@ -121,27 +118,25 @@ CleanerRef getPrevious() {
121118 return previous ;
122119 }
123120
124- void setPrevious (CleanerRef previous ) {
121+ void setPrevious (final CleanerRef previous ) {
125122 this .previous = previous ;
126123 }
127124
128125 CleanerRef getNext () {
129126 return next ;
130127 }
131128
132- void setNext (CleanerRef next ) {
129+ void setNext (final CleanerRef next ) {
133130 this .next = next ;
134131 }
135132 }
136133
137- public static interface Cleanable {
138- public void clean ();
134+ public interface Cleanable {
135+ void clean ();
139136 }
140137
141138 private class CleanerThread extends Thread {
142139
143- private static final long CLEANER_LINGER_TIME = 30000 ;
144-
145140 public CleanerThread () {
146141 super ("JNA Cleaner" );
147142 setDaemon (true );
@@ -151,20 +146,19 @@ public CleanerThread() {
151146 public void run () {
152147 while (true ) {
153148 try {
154- Reference <? extends Object > ref = referenceQueue .remove (CLEANER_LINGER_TIME );
149+ Reference <?> ref = referenceQueue .remove (CLEANER_LINGER_TIME );
155150 if (ref instanceof CleanerRef ) {
156151 ((CleanerRef ) ref ).clean ();
157152 } else if (ref == null ) {
158- synchronized (referenceQueue ) {
159- Logger logger = Logger .getLogger (Cleaner .class .getName ());
153+ synchronized (INSTANCE ) {
160154 if (firstCleanable == null ) {
161- cleanerThread = null ;
162155 logger .log (Level .FINE , "Shutting down CleanerThread" );
156+ cleanerRunning = false ;
163157 break ;
164158 } else if (logger .isLoggable (Level .FINER )) {
165159 StringBuilder registeredCleaners = new StringBuilder ();
166- for (CleanerRef cleanerRef = firstCleanable ; cleanerRef != null ; cleanerRef = cleanerRef .next ) {
167- if (registeredCleaners .length () != 0 ) {
160+ for (CleanerRef cleanerRef = firstCleanable ; cleanerRef != null ; cleanerRef = cleanerRef .next ) {
161+ if (registeredCleaners .length () != 0 ) {
168162 registeredCleaners .append (", " );
169163 }
170164 registeredCleaners .append (cleanerRef .cleanupTask .toString ());
@@ -178,6 +172,9 @@ public void run() {
178172 // our reference queue, well, there is no way to separate
179173 // the two cases.
180174 // https://groups.google.com/g/jna-users/c/j0fw96PlOpM/m/vbwNIb2pBQAJ
175+ synchronized (INSTANCE ) {
176+ cleanerRunning = false ;
177+ }
181178 break ;
182179 } catch (Exception ex ) {
183180 Logger .getLogger (Cleaner .class .getName ()).log (Level .SEVERE , null , ex );
0 commit comments