diff src/share/back/commonRef.c @ 0:37a05a11f281

Initial load
author duke
date Sat, 01 Dec 2007 00:00:00 +0000
parents
children 00cd9dc3c2b5
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/back/commonRef.c	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,607 @@
+/*
+ * Copyright 1998-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "util.h"
+#include "commonRef.h"
+
+#define ALL_REFS -1
+
+/*
+ * Each object sent to the front end is tracked with the RefNode struct
+ * (see util.h).
+ * External to this module, objects are identified by a jlong id which is
+ * simply the sequence number. A weak reference is usually used so that
+ * the presence of a debugger-tracked object will not prevent
+ * its collection. Once an object is collected, its RefNode may be
+ * deleted and the weak ref inside may be reused (these may happen in
+ * either order). Using the sequence number
+ * as the object id prevents ambiguity in the object id when the weak ref
+ * is reused. The RefNode* is stored with the object as it's JVMTI Tag.
+ *
+ * The ref member is changed from weak to strong when
+ * gc of the object is to be prevented.
+ * Whether or not it is strong, it is never exported from this module.
+ *
+ * A reference count of each jobject is also maintained here. It tracks
+ * the number times an object has been referenced through
+ * commonRef_refToID. A RefNode is freed once the reference
+ * count is decremented to 0 (with commonRef_release*), even if the
+ * correspoding object has not been collected.
+ *
+ * One hash table is maintained. The mapping of ID to jobject (or RefNode*)
+ * is handled with one hash table that will re-size itself as the number
+ * of RefNode's grow.
+ */
+
+/* Initial hash table size (must be power of 2) */
+#define HASH_INIT_SIZE 512
+/* If element count exceeds HASH_EXPAND_SCALE*hash_size we expand & re-hash */
+#define HASH_EXPAND_SCALE 8
+/* Maximum hash table size (must be power of 2) */
+#define HASH_MAX_SIZE  (1024*HASH_INIT_SIZE)
+
+/* Map a key (ID) to a hash bucket */
+static jint
+hashBucket(jlong key)
+{
+    /* Size should always be a power of 2, use mask instead of mod operator */
+    /*LINTED*/
+    return ((jint)key) & (gdata->objectsByIDsize-1);
+}
+
+/* Generate a new ID */
+static jlong
+newSeqNum(void)
+{
+    return gdata->nextSeqNum++;
+}
+
+/* Create a fresh RefNode structure, create a weak ref and tag the object */
+static RefNode *
+createNode(JNIEnv *env, jobject ref)
+{
+    RefNode   *node;
+    jobject    weakRef;
+    jvmtiError error;
+
+    /* Could allocate RefNode's in blocks, not sure it would help much */
+    node = (RefNode*)jvmtiAllocate((int)sizeof(RefNode));
+    if (node == NULL) {
+        return NULL;
+    }
+
+    /* Create weak reference to make sure we have a reference */
+    weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref);
+    if (weakRef == NULL) {
+        jvmtiDeallocate(node);
+        return NULL;
+    }
+
+    /* Set tag on weakRef */
+    error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)
+                          (gdata->jvmti, weakRef, ptr_to_jlong(node));
+    if ( error != JVMTI_ERROR_NONE ) {
+        JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef);
+        jvmtiDeallocate(node);
+        return NULL;
+    }
+
+    /* Fill in RefNode */
+    node->ref      = weakRef;
+    node->isStrong = JNI_FALSE;
+    node->count    = 1;
+    node->seqNum   = newSeqNum();
+
+    /* Count RefNode's created */
+    gdata->objectsByIDcount++;
+    return node;
+}
+
+/* Delete a RefNode allocation, delete weak/global ref and clear tag */
+static void
+deleteNode(JNIEnv *env, RefNode *node)
+{
+    LOG_MISC(("Freeing %d (%x)\n", (int)node->seqNum, node->ref));
+
+    if ( node->ref != NULL ) {
+        /* Clear tag */
+        (void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)
+                            (gdata->jvmti, node->ref, NULL_OBJECT_ID);
+        if (node->isStrong) {
+            JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
+        } else {
+            JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
+        }
+    }
+    gdata->objectsByIDcount--;
+    jvmtiDeallocate(node);
+}
+
+/* Change a RefNode to have a strong reference */
+static jobject
+strengthenNode(JNIEnv *env, RefNode *node)
+{
+    if (!node->isStrong) {
+        jobject strongRef;
+
+        strongRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, node->ref);
+        /*
+         * NewGlobalRef on a weak ref will return NULL if the weak
+         * reference has been collected or if out of memory.
+         * We need to distinguish those two occurrences.
+         */
+        if ((strongRef == NULL) && !isSameObject(env, node->ref, NULL)) {
+            EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");
+        }
+        if (strongRef != NULL) {
+            JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
+            node->ref      = strongRef;
+            node->isStrong = JNI_TRUE;
+        }
+        return strongRef;
+    } else {
+        return node->ref;
+    }
+}
+
+/* Change a RefNode to have a weak reference */
+static jweak
+weakenNode(JNIEnv *env, RefNode *node)
+{
+    if (node->isStrong) {
+        jweak weakRef;
+
+        weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, node->ref);
+        if (weakRef != NULL) {
+            JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
+            node->ref      = weakRef;
+            node->isStrong = JNI_FALSE;
+        }
+        return weakRef;
+    } else {
+        return node->ref;
+    }
+}
+
+/*
+ * Returns the node which contains the common reference for the
+ * given object. The passed reference should not be a weak reference
+ * managed in the object hash table (i.e. returned by commonRef_idToRef)
+ * because no sequence number checking is done.
+ */
+static RefNode *
+findNodeByRef(JNIEnv *env, jobject ref)
+{
+    jvmtiError error;
+    jlong      tag;
+
+    tag   = NULL_OBJECT_ID;
+    error = JVMTI_FUNC_PTR(gdata->jvmti,GetTag)(gdata->jvmti, ref, &tag);
+    if ( error == JVMTI_ERROR_NONE ) {
+        RefNode   *node;
+
+        node = (RefNode*)jlong_to_ptr(tag);
+        return node;
+    }
+    return NULL;
+}
+
+/* Locate and delete a node based on ID */
+static void
+deleteNodeByID(JNIEnv *env, jlong id, jint refCount)
+{
+    jint     slot;
+    RefNode *node;
+    RefNode *prev;
+
+    slot = hashBucket(id);
+    node = gdata->objectsByID[slot];
+    prev = NULL;
+
+    while (node != NULL) {
+        if (id == node->seqNum) {
+            if (refCount != ALL_REFS) {
+                node->count -= refCount;
+            } else {
+                node->count = 0;
+            }
+            if (node->count <= 0) {
+                if ( node->count < 0 ) {
+                    EXIT_ERROR(AGENT_ERROR_INTERNAL,"RefNode count < 0");
+                }
+                /* Detach from id hash table */
+                if (prev == NULL) {
+                    gdata->objectsByID[slot] = node->next;
+                } else {
+                    prev->next = node->next;
+                }
+                deleteNode(env, node);
+            }
+            break;
+        }
+        prev = node;
+        node = node->next;
+    }
+}
+
+/*
+ * Returns the node stored in the object hash table for the given object
+ * id. The id should be a value previously returned by
+ * commonRef_refToID.
+ *
+ *  NOTE: It is possible that a match is found here, but that the object
+ *        is garbage collected by the time the caller inspects node->ref.
+ *        Callers should take care using the node->ref object returned here.
+ *
+ */
+static RefNode *
+findNodeByID(JNIEnv *env, jlong id)
+{
+    jint     slot;
+    RefNode *node;
+    RefNode *prev;
+
+    slot = hashBucket(id);
+    node = gdata->objectsByID[slot];
+    prev = NULL;
+
+    while (node != NULL) {
+        if ( id == node->seqNum ) {
+            if ( prev != NULL ) {
+                /* Re-order hash list so this one is up front */
+                prev->next = node->next;
+                node->next = gdata->objectsByID[slot];
+                gdata->objectsByID[slot] = node;
+            }
+            break;
+        }
+        node = node->next;
+    }
+    return node;
+}
+
+/* Initialize the hash table stored in gdata area */
+static void
+initializeObjectsByID(int size)
+{
+    /* Size should always be a power of 2 */
+    if ( size > HASH_MAX_SIZE ) size = HASH_MAX_SIZE;
+    gdata->objectsByIDsize  = size;
+    gdata->objectsByIDcount = 0;
+    gdata->objectsByID      = (RefNode**)jvmtiAllocate((int)sizeof(RefNode*)*size);
+    (void)memset(gdata->objectsByID, 0, (int)sizeof(RefNode*)*size);
+}
+
+/* hash in a RefNode */
+static void
+hashIn(RefNode *node)
+{
+    jint     slot;
+
+    /* Add to id hashtable */
+    slot                     = hashBucket(node->seqNum);
+    node->next               = gdata->objectsByID[slot];
+    gdata->objectsByID[slot] = node;
+}
+
+/* Allocate and add RefNode to hash table */
+static RefNode *
+newCommonRef(JNIEnv *env, jobject ref)
+{
+    RefNode *node;
+
+    /* Allocate the node and set it up */
+    node = createNode(env, ref);
+    if ( node == NULL ) {
+        return NULL;
+    }
+
+    /* See if hash table needs expansion */
+    if ( gdata->objectsByIDcount > gdata->objectsByIDsize*HASH_EXPAND_SCALE &&
+         gdata->objectsByIDsize < HASH_MAX_SIZE ) {
+        RefNode **old;
+        int       oldsize;
+        int       newsize;
+        int       i;
+
+        /* Save old information */
+        old     = gdata->objectsByID;
+        oldsize = gdata->objectsByIDsize;
+        /* Allocate new hash table */
+        gdata->objectsByID = NULL;
+        newsize = oldsize*HASH_EXPAND_SCALE;
+        if ( newsize > HASH_MAX_SIZE ) newsize = HASH_MAX_SIZE;
+        initializeObjectsByID(newsize);
+        /* Walk over old one and hash in all the RefNodes */
+        for ( i = 0 ; i < oldsize ; i++ ) {
+            RefNode *onode;
+
+            onode = old[i];
+            while (onode != NULL) {
+                RefNode *next;
+
+                next = onode->next;
+                hashIn(onode);
+                onode = next;
+            }
+        }
+        jvmtiDeallocate(old);
+    }
+
+    /* Add to id hashtable */
+    hashIn(node);
+    return node;
+}
+
+/* Initialize the commonRefs usage */
+void
+commonRef_initialize(void)
+{
+    gdata->refLock = debugMonitorCreate("JDWP Reference Table Monitor");
+    gdata->nextSeqNum       = 1; /* 0 used for error indication */
+    initializeObjectsByID(HASH_INIT_SIZE);
+}
+
+/* Reset the commonRefs usage */
+void
+commonRef_reset(JNIEnv *env)
+{
+    debugMonitorEnter(gdata->refLock); {
+        int i;
+
+        for (i = 0; i < gdata->objectsByIDsize; i++) {
+            RefNode *node;
+
+            node = gdata->objectsByID[i];
+            while (node != NULL) {
+                RefNode *next;
+
+                next = node->next;
+                deleteNode(env, node);
+                node = next;
+            }
+            gdata->objectsByID[i] = NULL;
+        }
+
+        /* Toss entire hash table and re-create a new one */
+        jvmtiDeallocate(gdata->objectsByID);
+        gdata->objectsByID      = NULL;
+        gdata->nextSeqNum       = 1; /* 0 used for error indication */
+        initializeObjectsByID(HASH_INIT_SIZE);
+
+    } debugMonitorExit(gdata->refLock);
+}
+
+/*
+ * Given a reference obtained from JNI or JVMTI, return an object
+ * id suitable for sending to the debugger front end.
+ */
+jlong
+commonRef_refToID(JNIEnv *env, jobject ref)
+{
+    jlong id;
+
+    if (ref == NULL) {
+        return NULL_OBJECT_ID;
+    }
+
+    id = NULL_OBJECT_ID;
+    debugMonitorEnter(gdata->refLock); {
+        RefNode *node;
+
+        node = findNodeByRef(env, ref);
+        if (node == NULL) {
+            node = newCommonRef(env, ref);
+            if ( node != NULL ) {
+                id = node->seqNum;
+            }
+        } else {
+            id = node->seqNum;
+            node->count++;
+        }
+    } debugMonitorExit(gdata->refLock);
+    return id;
+}
+
+/*
+ * Given an object ID obtained from the debugger front end, return a
+ * strong, global reference to that object (or NULL if the object
+ * has been collected). The reference can then be used for JNI and
+ * JVMTI calls. Caller is resposible for deleting the returned reference.
+ */
+jobject
+commonRef_idToRef(JNIEnv *env, jlong id)
+{
+    jobject ref;
+
+    ref = NULL;
+    debugMonitorEnter(gdata->refLock); {
+        RefNode *node;
+
+        node = findNodeByID(env, id);
+        if (node != NULL) {
+            if (node->isStrong) {
+                saveGlobalRef(env, node->ref, &ref);
+            } else {
+                jobject lref;
+
+                lref = JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref);
+                if ( lref == NULL ) {
+                    /* Object was GC'd shortly after we found the node */
+                    deleteNodeByID(env, node->seqNum, ALL_REFS);
+                } else {
+                    saveGlobalRef(env, node->ref, &ref);
+                    JNI_FUNC_PTR(env,DeleteLocalRef)(env, lref);
+                }
+            }
+        }
+    } debugMonitorExit(gdata->refLock);
+    return ref;
+}
+
+/* Deletes the global reference that commonRef_idToRef() created */
+void
+commonRef_idToRef_delete(JNIEnv *env, jobject ref)
+{
+    if ( ref==NULL ) {
+        return;
+    }
+    tossGlobalRef(env, &ref);
+}
+
+
+/* Prevent garbage collection of an object */
+jvmtiError
+commonRef_pin(jlong id)
+{
+    jvmtiError error;
+
+    error = JVMTI_ERROR_NONE;
+    if (id == NULL_OBJECT_ID) {
+        return error;
+    }
+    debugMonitorEnter(gdata->refLock); {
+        JNIEnv  *env;
+        RefNode *node;
+
+        env  = getEnv();
+        node = findNodeByID(env, id);
+        if (node == NULL) {
+            error = AGENT_ERROR_INVALID_OBJECT;
+        } else {
+            jobject strongRef;
+
+            strongRef = strengthenNode(env, node);
+            if (strongRef == NULL) {
+                /*
+                 * Referent has been collected, clean up now.
+                 */
+                error = AGENT_ERROR_INVALID_OBJECT;
+                deleteNodeByID(env, id, ALL_REFS);
+            }
+        }
+    } debugMonitorExit(gdata->refLock);
+    return error;
+}
+
+/* Permit garbage collection of an object */
+jvmtiError
+commonRef_unpin(jlong id)
+{
+    jvmtiError error;
+
+    error = JVMTI_ERROR_NONE;
+    debugMonitorEnter(gdata->refLock); {
+        JNIEnv  *env;
+        RefNode *node;
+
+        env  = getEnv();
+        node = findNodeByID(env, id);
+        if (node != NULL) {
+            jweak weakRef;
+
+            weakRef = weakenNode(env, node);
+            if (weakRef == NULL) {
+                error = AGENT_ERROR_OUT_OF_MEMORY;
+            }
+        }
+    } debugMonitorExit(gdata->refLock);
+    return error;
+}
+
+/* Release tracking of an object by ID */
+void
+commonRef_release(JNIEnv *env, jlong id)
+{
+    debugMonitorEnter(gdata->refLock); {
+        deleteNodeByID(env, id, 1);
+    } debugMonitorExit(gdata->refLock);
+}
+
+void
+commonRef_releaseMultiple(JNIEnv *env, jlong id, jint refCount)
+{
+    debugMonitorEnter(gdata->refLock); {
+        deleteNodeByID(env, id, refCount);
+    } debugMonitorExit(gdata->refLock);
+}
+
+/* Get rid of RefNodes for objects that no longer exist */
+void
+commonRef_compact(void)
+{
+    JNIEnv  *env;
+    RefNode *node;
+    RefNode *prev;
+    int      i;
+
+    env = getEnv();
+    debugMonitorEnter(gdata->refLock); {
+        if ( gdata->objectsByIDsize > 0 ) {
+            /*
+             * Walk through the id-based hash table. Detach any nodes
+             * for which the ref has been collected.
+             */
+            for (i = 0; i < gdata->objectsByIDsize; i++) {
+                node = gdata->objectsByID[i];
+                prev = NULL;
+                while (node != NULL) {
+                    /* Has the object been collected? */
+                    if ( (!node->isStrong) &&
+                          isSameObject(env, node->ref, NULL)) {
+                        RefNode *freed;
+
+                        /* Detach from the ID list */
+                        if (prev == NULL) {
+                            gdata->objectsByID[i] = node->next;
+                        } else {
+                            prev->next = node->next;
+                        }
+                        freed = node;
+                        node = node->next;
+                        deleteNode(env, freed);
+                    } else {
+                        prev = node;
+                        node = node->next;
+                    }
+                }
+            }
+        }
+    } debugMonitorExit(gdata->refLock);
+}
+
+/* Lock the commonRef tables */
+void
+commonRef_lock(void)
+{
+    debugMonitorEnter(gdata->refLock);
+}
+
+/* Unlock the commonRef tables */
+void
+commonRef_unlock(void)
+{
+    debugMonitorExit(gdata->refLock);
+}