Skip to content

Commit 98caf53

Browse files
low-contention refactored reference Cleaner
1 parent 0da700d commit 98caf53

File tree

1 file changed

+43
-81
lines changed

1 file changed

+43
-81
lines changed

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

Lines changed: 43 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727
import java.lang.ref.PhantomReference;
2828
import java.lang.ref.Reference;
2929
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;
3034
import java.util.logging.Level;
3135
import 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

Comments
 (0)