summaryrefslogtreecommitdiffstats
path: root/jdisc_core/src/main/java/com/yahoo/jdisc/AbstractResource.java
diff options
context:
space:
mode:
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.java175
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(); }
}
}