@@ -45,35 +45,11 @@ public static Cleaner getCleaner() {
4545 }
4646
4747 private final ReferenceQueue <Object > referenceQueue ;
48- private final Thread cleanerThread ;
48+ private Thread cleanerThread ;
4949 private CleanerRef firstCleanable ;
5050
5151 private Cleaner () {
5252 referenceQueue = new ReferenceQueue <Object >();
53- cleanerThread = new Thread () {
54- @ Override
55- public void run () {
56- while (true ) {
57- try {
58- Reference <? extends Object > ref = referenceQueue .remove ();
59- if (ref instanceof CleanerRef ) {
60- ((CleanerRef ) ref ).clean ();
61- }
62- } catch (InterruptedException ex ) {
63- // Can be raised on shutdown. If anyone else messes with
64- // our reference queue, well, there is no way to separate
65- // the two cases.
66- // https://groups.google.com/g/jna-users/c/j0fw96PlOpM/m/vbwNIb2pBQAJ
67- break ;
68- } catch (Exception ex ) {
69- Logger .getLogger (Cleaner .class .getName ()).log (Level .SEVERE , null , ex );
70- }
71- }
72- }
73- };
74- cleanerThread .setName ("JNA Cleaner" );
75- cleanerThread .setDaemon (true );
76- cleanerThread .start ();
7753 }
7854
7955 public synchronized Cleanable register (Object obj , Runnable cleanupTask ) {
@@ -83,34 +59,43 @@ public synchronized Cleanable register(Object obj, Runnable cleanupTask) {
8359 }
8460
8561 private synchronized CleanerRef add (CleanerRef ref ) {
86- if (firstCleanable == null ) {
87- firstCleanable = ref ;
88- } else {
89- ref .setNext (firstCleanable );
90- firstCleanable .setPrevious (ref );
91- firstCleanable = 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 ;
9276 }
93- return ref ;
9477 }
9578
9679 private synchronized boolean remove (CleanerRef ref ) {
97- boolean inChain = false ;
98- if (ref == firstCleanable ) {
99- firstCleanable = ref .getNext ();
100- inChain = true ;
101- }
102- if (ref .getPrevious () != null ) {
103- ref .getPrevious ().setNext (ref .getNext ());
104- }
105- if (ref .getNext () != null ) {
106- ref .getNext ().setPrevious (ref .getPrevious ());
107- }
108- if (ref .getPrevious () != null || ref .getNext () != null ) {
109- inChain = true ;
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 ;
11098 }
111- ref .setNext (null );
112- ref .setPrevious (null );
113- return inChain ;
11499 }
115100
116101 private static class CleanerRef extends PhantomReference <Object > implements Cleanable {
@@ -125,6 +110,7 @@ public CleanerRef(Cleaner cleaner, Object referent, ReferenceQueue<? super Objec
125110 this .cleanupTask = cleanupTask ;
126111 }
127112
113+ @ Override
128114 public void clean () {
129115 if (cleaner .remove (this )) {
130116 cleanupTask .run ();
@@ -151,4 +137,52 @@ void setNext(CleanerRef next) {
151137 public static interface Cleanable {
152138 public void clean ();
153139 }
140+
141+ private class CleanerThread extends Thread {
142+
143+ private static final long CLEANER_LINGER_TIME = 30000 ;
144+
145+ public CleanerThread () {
146+ super ("JNA Cleaner" );
147+ setDaemon (true );
148+ }
149+
150+ @ Override
151+ public void run () {
152+ while (true ) {
153+ try {
154+ Reference <? extends Object > ref = referenceQueue .remove (CLEANER_LINGER_TIME );
155+ if (ref instanceof CleanerRef ) {
156+ ((CleanerRef ) ref ).clean ();
157+ } 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 ());
171+ }
172+ logger .log (Level .FINER , "Registered Cleaners: {0}" , registeredCleaners .toString ());
173+ }
174+ }
175+ }
176+ } catch (InterruptedException ex ) {
177+ // Can be raised on shutdown. If anyone else messes with
178+ // our reference queue, well, there is no way to separate
179+ // the two cases.
180+ // https://groups.google.com/g/jna-users/c/j0fw96PlOpM/m/vbwNIb2pBQAJ
181+ break ;
182+ } catch (Exception ex ) {
183+ Logger .getLogger (Cleaner .class .getName ()).log (Level .SEVERE , null , ex );
184+ }
185+ }
186+ }
187+ }
154188}
0 commit comments