diff options
Diffstat (limited to 'jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java')
-rw-r--r-- | jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java | 175 |
1 files changed, 25 insertions, 150 deletions
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java b/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java index 05c3582a541..a5b5dad2583 100644 --- a/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java +++ b/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java @@ -2,16 +2,13 @@ package com.yahoo.jdisc; import com.yahoo.jdisc.handler.RequestHandler; +import com.yahoo.jdisc.refcount.DebugReferencesByContextMap; +import com.yahoo.jdisc.refcount.DebugReferencesWithStack; +import com.yahoo.jdisc.refcount.DestructableResource; +import com.yahoo.jdisc.refcount.ReferencesByCount; import com.yahoo.jdisc.service.ClientProvider; import com.yahoo.jdisc.service.ServerProvider; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.logging.Level; -import java.util.logging.Logger; +import com.yahoo.jdisc.refcount.References; /** * This class provides a thread-safe implementation of the {@link SharedResource} interface, and should be used for @@ -22,75 +19,33 @@ import java.util.logging.Logger; */ public abstract class AbstractResource implements SharedResource { - private static final Logger log = Logger.getLogger(AbstractResource.class.getName()); + private static final Debug debug = DEBUG; - private final boolean debug = SharedResource.DEBUG; - private final AtomicInteger refCount; - private final Object monitor; - private final Set<Throwable> activeReferences; - private final ResourceReference initialCreationReference; + private final References references; protected AbstractResource() { - if (!debug) { - this.refCount = new AtomicInteger(1); - this.monitor = null; - this.activeReferences = null; - this.initialCreationReference = new NoDebugResourceReference(this); + DestructableResource destructable = new WrappedResource(this); + if (debug == Debug.SIMPLE) { + references = new DebugReferencesByContextMap(destructable, this); + } else if (debug == Debug.STACK) { + references = new DebugReferencesWithStack(destructable); } else { - this.refCount = null; - this.monitor = new Object(); - this.activeReferences = new HashSet<>(); - final Throwable referenceStack = new Throwable(); - this.activeReferences.add(referenceStack); - this.initialCreationReference = new DebugResourceReference(this, referenceStack); + references = new ReferencesByCount(destructable); } } @Override public final ResourceReference refer() { - if (!debug) { - addRef(1); - return new NoDebugResourceReference(this); - } - - final Throwable referenceStack = new Throwable(); - final String state; - synchronized (monitor) { - if (activeReferences.isEmpty()) { - throw new IllegalStateException("Object is already destroyed, no more new references may be created." - + " State={ " + currentStateDebugWithLock() + " }"); - } - activeReferences.add(referenceStack); - state = currentStateDebugWithLock(); - } - log.log(Level.FINE, referenceStack, () -> - getClass().getName() + "@" + System.identityHashCode(this) + ".refer(): state={ " + state + " }"); - return new DebugResourceReference(this, referenceStack); + return refer(null); } - @Override - public final void release() { - initialCreationReference.close(); + public final ResourceReference refer(Object context) { + return references.refer(context); } - private void removeReferenceStack(final Throwable referenceStack, final Throwable releaseStack) { - final boolean doDestroy; - final String state; - synchronized (monitor) { - final boolean wasThere = activeReferences.remove(referenceStack); - state = currentStateDebugWithLock(); - if (!wasThere) { - throw new IllegalStateException("Reference is already released and can only be released once." - + " reference=" + Arrays.toString(referenceStack.getStackTrace()) - + ". State={ " + state + "}"); - } - doDestroy = activeReferences.isEmpty(); - } - log.log(Level.FINE, releaseStack, - () ->getClass().getName() + "@" + System.identityHashCode(this) + " release: state={ " + state + " }"); - if (doDestroy) { - destroy(); - } + @Override + public final void release() { + references.release(); } /** @@ -100,105 +55,25 @@ public abstract class AbstractResource implements SharedResource { * @return The current value of the reference counter. */ public final int retainCount() { - if (!debug) { - return refCount.get(); - } - - synchronized (monitor) { - return activeReferences.size(); - } + return references.referenceCount(); } /** * <p>This method signals that this AbstractResource can dispose of any internal resources, and commence with shut * down of any internal threads. This will be called once the reference count of this resource reaches zero.</p> */ - protected void destroy() { - - } - - private int addRef(int value) { - while (true) { - int prev = refCount.get(); - if (prev == 0) { - throw new IllegalStateException(getClass().getName() + ".addRef(" + value + "):" - + " Object is already destroyed." - + " Consider toggling the " + SharedResource.SYSTEM_PROPERTY_NAME_DEBUG - + " system property to get debugging assistance with reference tracking."); - } - int next = prev + value; - if (refCount.compareAndSet(prev, next)) { - return next; - } - } - } + protected void destroy() { } /** * Returns a string describing the current state of references in human-friendly terms. May be used for debugging. */ public String currentState() { - if (!debug) { - return "Active references: " + refCount.get() + "." - + " Resource reference debugging is turned off. Consider toggling the " - + SharedResource.SYSTEM_PROPERTY_NAME_DEBUG - + " system property to get debugging assistance with reference tracking."; - } - synchronized (monitor) { - return currentStateDebugWithLock(); - } - } - - private String currentStateDebugWithLock() { - return "Active references: " + makeListOfActiveReferences(); - } - - private String makeListOfActiveReferences() { - final StringBuilder builder = new StringBuilder(); - builder.append("["); - for (final Throwable activeReference : activeReferences) { - builder.append(" "); - builder.append(Arrays.toString(activeReference.getStackTrace())); - } - builder.append(" ]"); - return builder.toString(); - } - - private static class NoDebugResourceReference implements ResourceReference { - private final AbstractResource resource; - private final AtomicBoolean isReleased = new AtomicBoolean(false); - - public NoDebugResourceReference(final AbstractResource resource) { - this.resource = resource; - } - - @Override - public final void close() { - final boolean wasReleasedBefore = isReleased.getAndSet(true); - if (wasReleasedBefore) { - final String message = "Reference is already released and can only be released once." - + " State={ " + resource.currentState() + " }"; - throw new IllegalStateException(message); - } - int refCount = resource.addRef(-1); - if (refCount == 0) { - resource.destroy(); - } - } + return references.currentState(); } - private static class DebugResourceReference implements ResourceReference { + static private class WrappedResource implements DestructableResource { private final AbstractResource resource; - private final Throwable referenceStack; - - public DebugResourceReference(final AbstractResource resource, final Throwable referenceStack) { - this.resource = resource; - this.referenceStack = referenceStack; - } - - @Override - public final void close() { - final Throwable releaseStack = new Throwable(); - resource.removeReferenceStack(referenceStack, releaseStack); - } + WrappedResource(AbstractResource resource) { this.resource = resource; } + @Override public void close() { resource.destroy(); } } } |