Skip to content

Commit f0da2f8

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

File tree

1 file changed

+45
-81
lines changed

1 file changed

+45
-81
lines changed

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

Lines changed: 45 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,65 @@ 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;
5054

5155
private Cleaner() {
5256
referenceQueue = new ReferenceQueue<>();
57+
uncleaned = ConcurrentHashMap.newKeySet();
58+
cleanerRunning = new AtomicBoolean(false);
5359
}
5460

55-
public synchronized Cleanable register(Object obj, Runnable cleanupTask) {
61+
public Cleanable register(Object obj, Runnable cleanupTask) {
5662
// The important side effect is the PhantomReference, that is yielded
5763
// after the referent is GCed
58-
return add(new CleanerRef(this, obj, referenceQueue, cleanupTask));
59-
}
64+
Cleanable cleanable = add(new CleanerRef(obj, referenceQueue, cleanupTask));
6065

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();
7670
}
71+
72+
return cleanable;
7773
}
7874

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);
9983
}
10084

10185
private static class CleanerRef extends PhantomReference<Object> implements Cleanable {
102-
private final Cleaner cleaner;
10386
private final Runnable cleanupTask;
104-
private CleanerRef previous;
105-
private CleanerRef next;
87+
private final AtomicBoolean cleaned;
10688

107-
public CleanerRef(Cleaner cleaner, Object referent, ReferenceQueue<? super Object> q, Runnable cleanupTask) {
89+
CleanerRef(Object referent, ReferenceQueue<? super Object> q, Runnable cleanupTask) {
10890
super(referent, q);
109-
this.cleaner = cleaner;
11091
this.cleanupTask = cleanupTask;
92+
this.cleaned = new AtomicBoolean(false);
11193
}
11294

11395
@Override
11496
public void clean() {
115-
if(cleaner.remove(this)) {
97+
if (cleaned.compareAndSet(false, true)) {
98+
INSTANCE.remove(this);
11699
cleanupTask.run();
117100
}
118101
}
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-
}
135102
}
136103

137-
public static interface Cleanable {
138-
public void clean();
104+
public interface Cleanable {
105+
void clean();
139106
}
140107

141108
private class CleanerThread extends Thread {
142109

143-
private static final long CLEANER_LINGER_TIME = 30000;
110+
private final long CLEANER_LINGER_TIME = TimeUnit.SECONDS.toMillis(30L);
144111

145112
public CleanerThread() {
146113
super("JNA Cleaner");
@@ -151,26 +118,23 @@ public CleanerThread() {
151118
public void run() {
152119
while (true) {
153120
try {
154-
Reference<? extends Object> ref = referenceQueue.remove(CLEANER_LINGER_TIME);
121+
Reference<?> ref = referenceQueue.remove(CLEANER_LINGER_TIME);
155122
if (ref instanceof CleanerRef) {
156123
((CleanerRef) ref).clean();
157124
} 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(", ");
171134
}
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());
174138
}
175139
}
176140
} catch (InterruptedException ex) {

0 commit comments

Comments
 (0)