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,63 @@ 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 = new AtomicBoolean ( false ) ;
5054
5155 private Cleaner () {
5256 referenceQueue = new ReferenceQueue <>();
57+ uncleaned = ConcurrentHashMap .newKeySet ();
5358 }
5459
55- public synchronized Cleanable register (Object obj , Runnable cleanupTask ) {
60+ public Cleanable register (Object obj , Runnable cleanupTask ) {
5661 // The important side effect is the PhantomReference, that is yielded
5762 // after the referent is GCed
58- return add (new CleanerRef (this , obj , referenceQueue , cleanupTask ));
59- }
63+ Cleanable cleanable = add (new CleanerRef (obj , referenceQueue , cleanupTask ));
6064
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 ;
65+ if (cleanerRunning .compareAndSet (false , true )) {
66+ Logger .getLogger (Cleaner .class .getName ()).log (Level .FINE , "Starting CleanerThread" );
67+ Thread cleanerThread = new CleanerThread ();
68+ cleanerThread .start ();
7669 }
70+
71+ return cleanable ;
7772 }
7873
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- }
74+ private CleanerRef add (final CleanerRef toAdd ) {
75+ uncleaned .add (toAdd );
76+ return toAdd ;
77+ }
78+
79+ // Remove by node reference
80+ private void remove (final CleanerRef node ) {
81+ uncleaned .remove (node );
9982 }
10083
10184 private static class CleanerRef extends PhantomReference <Object > implements Cleanable {
102- private final Cleaner cleaner ;
10385 private final Runnable cleanupTask ;
104- private CleanerRef previous ;
105- private CleanerRef next ;
86+ private final AtomicBoolean cleaned = new AtomicBoolean (false );
10687
107- public CleanerRef (Cleaner cleaner , Object referent , ReferenceQueue <? super Object > q , Runnable cleanupTask ) {
88+ CleanerRef (Object referent , ReferenceQueue <? super Object > q , Runnable cleanupTask ) {
10889 super (referent , q );
109- this .cleaner = cleaner ;
11090 this .cleanupTask = cleanupTask ;
11191 }
11292
11393 @ Override
11494 public void clean () {
115- if (cleaner .remove (this )) {
95+ if (cleaned .compareAndSet (false , true )) {
96+ INSTANCE .remove (this );
11697 cleanupTask .run ();
11798 }
11899 }
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- }
135100 }
136101
137- public static interface Cleanable {
138- public void clean ();
102+ public interface Cleanable {
103+ void clean ();
139104 }
140105
141106 private class CleanerThread extends Thread {
142107
143- private static final long CLEANER_LINGER_TIME = 30000 ;
108+ private final long CLEANER_LINGER_TIME = TimeUnit . SECONDS . toMillis ( 30L ) ;
144109
145110 public CleanerThread () {
146111 super ("JNA Cleaner" );
@@ -151,26 +116,23 @@ public CleanerThread() {
151116 public void run () {
152117 while (true ) {
153118 try {
154- Reference <? extends Object > ref = referenceQueue .remove (CLEANER_LINGER_TIME );
119+ Reference <?> ref = referenceQueue .remove (CLEANER_LINGER_TIME );
155120 if (ref instanceof CleanerRef ) {
156121 ((CleanerRef ) ref ).clean ();
157122 } 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 ());
123+ Logger logger = Logger .getLogger (Cleaner .class .getName ());
124+ if (cleanerRunning .compareAndSet (uncleaned .isEmpty (), false )) {
125+ logger .log (Level .FINE , "Shutting down CleanerThread" );
126+ break ;
127+ } else if (logger .isLoggable (Level .FINER )) {
128+ StringBuilder registeredCleaners = new StringBuilder ();
129+ uncleaned .forEach ((cleanerRef ) -> {
130+ if (registeredCleaners .length () != 0 ) {
131+ registeredCleaners .append (", " );
171132 }
172- logger .log (Level .FINER , "Registered Cleaners: {0}" , registeredCleaners .toString ());
173- }
133+ registeredCleaners .append (cleanerRef .cleanupTask .toString ());
134+ });
135+ logger .log (Level .FINER , "Registered Cleaners: {0}" , registeredCleaners .toString ());
174136 }
175137 }
176138 } catch (InterruptedException ex ) {
0 commit comments