27
27
import java .lang .ref .PhantomReference ;
28
28
import java .lang .ref .Reference ;
29
29
import 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 ;
30
34
import java .util .logging .Level ;
31
35
import java .util .logging .Logger ;
32
36
@@ -45,102 +49,65 @@ public static Cleaner getCleaner() {
45
49
}
46
50
47
51
private final ReferenceQueue <Object > referenceQueue ;
48
- private Thread cleanerThread ;
49
- private CleanerRef firstCleanable ;
52
+ private final Set < CleanerRef > uncleaned ;
53
+ private final AtomicBoolean cleanerRunning ;
50
54
51
55
private Cleaner () {
52
56
referenceQueue = new ReferenceQueue <>();
57
+ uncleaned = ConcurrentHashMap .newKeySet ();
58
+ cleanerRunning = new AtomicBoolean (false );
53
59
}
54
60
55
- public synchronized Cleanable register (Object obj , Runnable cleanupTask ) {
61
+ public Cleanable register (Object obj , Runnable cleanupTask ) {
56
62
// The important side effect is the PhantomReference, that is yielded
57
63
// after the referent is GCed
58
- return add (new CleanerRef (this , obj , referenceQueue , cleanupTask ));
59
- }
64
+ Cleanable cleanable = add (new CleanerRef (obj , referenceQueue , cleanupTask ));
60
65
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 ();
76
70
}
71
+
72
+ return cleanable ;
77
73
}
78
74
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 );
99
83
}
100
84
101
85
private static class CleanerRef extends PhantomReference <Object > implements Cleanable {
102
- private final Cleaner cleaner ;
103
86
private final Runnable cleanupTask ;
104
- private CleanerRef previous ;
105
- private CleanerRef next ;
87
+ private final AtomicBoolean cleaned ;
106
88
107
- public CleanerRef (Cleaner cleaner , Object referent , ReferenceQueue <? super Object > q , Runnable cleanupTask ) {
89
+ CleanerRef (Object referent , ReferenceQueue <? super Object > q , Runnable cleanupTask ) {
108
90
super (referent , q );
109
- this .cleaner = cleaner ;
110
91
this .cleanupTask = cleanupTask ;
92
+ this .cleaned = new AtomicBoolean (false );
111
93
}
112
94
113
95
@ Override
114
96
public void clean () {
115
- if (cleaner .remove (this )) {
97
+ if (cleaned .compareAndSet (false , true )) {
98
+ INSTANCE .remove (this );
116
99
cleanupTask .run ();
117
100
}
118
101
}
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
- }
135
102
}
136
103
137
- public static interface Cleanable {
138
- public void clean ();
104
+ public interface Cleanable {
105
+ void clean ();
139
106
}
140
107
141
108
private class CleanerThread extends Thread {
142
109
143
- private static final long CLEANER_LINGER_TIME = 30000 ;
110
+ private final long CLEANER_LINGER_TIME = TimeUnit . SECONDS . toMillis ( 30L ) ;
144
111
145
112
public CleanerThread () {
146
113
super ("JNA Cleaner" );
@@ -151,26 +118,23 @@ public CleanerThread() {
151
118
public void run () {
152
119
while (true ) {
153
120
try {
154
- Reference <? extends Object > ref = referenceQueue .remove (CLEANER_LINGER_TIME );
121
+ Reference <?> ref = referenceQueue .remove (CLEANER_LINGER_TIME );
155
122
if (ref instanceof CleanerRef ) {
156
123
((CleanerRef ) ref ).clean ();
157
124
} 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 (", " );
171
134
}
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 ());
174
138
}
175
139
}
176
140
} catch (InterruptedException ex ) {
0 commit comments