ObjectCleaner may indefinitely block on ReferenceQueue#poll

Motivation:
ObjectCleaner polls a ReferenceQueue which will block indefinitely. However it is possible there is a race condition between the live set of objects being empty due to the WeakReference being cleaned/cleared and polling the queue. If this situation occurs the cleanup thread may never unblock if no more objects are added to the live set, and may result in an application's failure to gracefully close.

Modifications:
- ReferenceQueue.remove should use a timeout to compensate for the race condition, and avoid dead lock

Result:
No more dead lock in ObjectCleaner when polling the ReferenceQueue.
This commit is contained in:
Scott Mitchell 2018-01-19 08:15:50 -08:00 committed by Norman Maurer
parent bd6435f553
commit 9466593bbd

View File

@ -22,12 +22,16 @@ import java.lang.ref.WeakReference;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import static io.netty.util.internal.SystemPropertyUtil.getInt;
import static java.lang.Math.max;
/**
* Allows a way to register some {@link Runnable} that will executed once there are no references to an {@link Object}
* anymore.
*/
public final class ObjectCleaner {
private static final int REFERENCE_QUEUE_POLL_TIMEOUT_MS =
max(500, getInt("io.netty.util.internal.ObjectCleaner.refQueuePollTimeout", 10000));
// This will hold a reference to the AutomaticCleanerReference which will be removed once we called cleanup()
private static final Set<AutomaticCleanerReference> LIVE_SET = new ConcurrentSet<AutomaticCleanerReference>();
private static final ReferenceQueue<Object> REFERENCE_QUEUE = new ReferenceQueue<Object>();
@ -43,11 +47,13 @@ public final class ObjectCleaner {
while (!LIVE_SET.isEmpty()) {
try {
AutomaticCleanerReference reference =
(AutomaticCleanerReference) REFERENCE_QUEUE.remove();
try {
reference.cleanup();
} finally {
LIVE_SET.remove(reference);
(AutomaticCleanerReference) REFERENCE_QUEUE.remove(REFERENCE_QUEUE_POLL_TIMEOUT_MS);
if (reference != null) {
try {
reference.cleanup();
} finally {
LIVE_SET.remove(reference);
}
}
} catch (InterruptedException ex) {
// Just consume and move on