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