/* * This file is part of the HyperGraphDB source distribution. This is copyrighted * software. For permitted uses, licensing options and redistribution, please see * the LicensingInformation file at the root level of the distribution. * * Copyright (c) 2005-2010 Kobrix Software, Inc. All rights reserved. */ package org.hypergraphdb.handle; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import org.hypergraphdb.HGHandle; import org.hypergraphdb.HGPersistentHandle; public class WeakHandle extends WeakReference<Object> implements HGLiveHandle, Comparable<HGHandle> { public static ThreadLocal<Boolean> returnEnqueued = new ThreadLocal<Boolean>(); private HGPersistentHandle persistentHandle; private byte flags; public WeakHandle(Object ref, HGPersistentHandle persistentHandle, byte flags, ReferenceQueue<Object> refQueue) { super(ref, refQueue); this.persistentHandle = persistentHandle; this.flags = flags; } public byte getFlags() { return flags; } public HGPersistentHandle getPersistent() { return persistentHandle; } public Object getRef() { // // Here, we want to return null when the object is about to be garbage // collected. This will be indicated by the fact that the object will // be enqueued in the ReferenceQueue for this WeakReference. // // Obviously, we are doing something not orthodox here. The main danger // and the first thing to examine in case of weird behavior with this // is a situation where an atom gets removed from the 'atoms' WeakHashMap // in the cache (hence it's being garbage collected) and 'getRef' is called // after that but before the reference gets enqueued. Is that possible at all? // It looks like the GC manages references and references queues in a high-priority // thread. Also, when an object is going to be garbage collect its references are // added to a "pending" list from where they are further enqueued. The 'isEnqueued' // method actually checks for "pending or already enqueued" so we should be fine. // // However, there is a special case in which we don't want to return null: when // we are in the weak reference queue cleanup thread. Because cleanup may involve // a call to 'getRef', this may result in a deadlock within the ref // cleanup thread itself. For this we use the thread local variable 'returnEnqueued' // Object x = get(); if (isEnqueued()) { Boolean f = returnEnqueued.get(); if (f != null && f.booleanValue()) return x; x = null; do { try { synchronized (this) { wait(100); } } catch (InterruptedException ex) { } } while (isEnqueued()); } return x; } public void accessed() { } public final int hashCode() { return persistentHandle.hashCode(); } public final boolean equals(Object other) { if (other == null || ! (other instanceof HGHandle)) return false; return persistentHandle.equals(((HGHandle)other).getPersistent()); } public String toString() { return "weakHandle(" + persistentHandle.toString() + ")"; } public int compareTo(HGHandle h) { if (h instanceof HGPersistentHandle) return this.persistentHandle.compareTo((HGPersistentHandle)h); else return this.persistentHandle.compareTo(((HGLiveHandle)h).getPersistent()); } }