io.permazen.util.CloseableTracker Maven / Gradle / Ivy
Show all versions of permazen-util Show documentation
/*
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
*/
package io.permazen.util;
import com.google.common.base.Preconditions;
import java.io.Closeable;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Keeps track of {@link Closeable} objects (termed items) that must be reliably {@linkplain Closeable#close closed}
* prior to shutting down some associated context, but that also must not be closed prior to becoming unreachable, when
* the straightforward strategy of just storing them until context shutdown would require too much memory. In a
* typical scenario, these items are returned (indirectly by reference from some associated holder object) from some
* method to callers who cannot be expected to reliably close them. Following the simple strategy of just storing all unclosed
* items until context shutdown means unbounded memory use can occur; this class solves that problem.
*
*
* For a concrete example, consider a method that returns an {@link java.util.Iterator}, where the returned iterator
* is implemented based on some {@link Closeable} internal resource. Since {@link java.util.Iterator} has no {@code close()}
* method, the caller of the method cannot be expected to "close" the {@link java.util.Iterator} (and in turn, the internal
* resource), when done with it. Such a method could use {@link #add add(iterator, resource)} to close this leak.
*
*
* For each such context, an instance of this class may be used to register and track the associated items,
* guaranteeing that they will always eventually be closed, but doing so in a way that avoids memory leaks:
* For each item, there must be a corresponding holder object containing a reference to it. The holder objects are
* then tracked by this class using weak references. When a holder object is no longer strongly referenced, the corresponding
* item is closed. With this scheme, no memory leak occurs due to this tracking, even when arbitrarily many items are created.
* This of course assumes that when a holder object is no longer referenced, the associated item may be safely closed.
*
*
* A registered {@link Closeable} item is closed at the first occurrence of any of the following:
*
* - The application itself invokes {@link Closeable#close Closeable.close()} on the item
* - The associated holder object is no longer strongly referenced, and then {@link #poll} is invoked
* - {@link CloseableTracker#close} is invoked
* - {@link CloseableTracker#finalize} is invoked (i.e., this instance is garbage collected)
*
*
*
* Note that {@link Closeable#close Closeable.close()} is required to be idempotent, so application usage
* that results in eager closing of items is appropriate and encouraged. Use of this class may occasionally
* result in {@link Closeable#close Closeable.close()} being invoked more than once for registered items.
*
*
* Instances of this class are thread safe.
*/
public class CloseableTracker implements Closeable {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
private HashSet unclosedItems;
private ReferenceQueue