Skip to content

Commit 79a08bd

Browse files
fixes #1616 tighten up synchronization and conditional logic
1 parent 0da700d commit 79a08bd

File tree

1 file changed

+55
-58
lines changed

1 file changed

+55
-58
lines changed

src/com/sun/jna/internal/Cleaner.java

Lines changed: 55 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.lang.ref.PhantomReference;
2828
import java.lang.ref.Reference;
2929
import java.lang.ref.ReferenceQueue;
30+
import java.util.concurrent.TimeUnit;
3031
import java.util.logging.Level;
3132
import java.util.logging.Logger;
3233

@@ -35,84 +36,80 @@
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
*/
4041
public 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

Comments
 (0)