OSDN Git Service

Merge change Iadbb9163
authorAndroid (Google) Code Review <android-gerrit@google.com>
Tue, 20 Oct 2009 22:25:32 +0000 (18:25 -0400)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Tue, 20 Oct 2009 22:25:32 +0000 (18:25 -0400)
* changes:
  Implement JNI "weak global" references.

docs/jni-tips.html
vm/CheckJni.c
vm/Globals.h
vm/Jni.c
vm/JniInternal.h
vm/Native.c
vm/alloc/MarkSweep.c

index 56136e8..6d7fa55 100644 (file)
@@ -528,10 +528,11 @@ that use 64-bit pointers, <strong>you need to stash your native pointers in a
     Java bytecodes or class files, so passing in binary class data
     doesn't work.  Translation facilities may be added in a future
     version of the VM.</li>
-    <li><code>NewWeakGlobalRef</code> and <code>DeleteWeakGlobalRef</code>
-    are not implemented.  The
-    VM supports weak references, but not JNI "weak global" references.
-    These will be supported in a future release.</li>
+    <li>"Weak global" references are implemented, but may only be passed
+    to <code>NewLocalRef</code>, <code>NewGlobalRef</code>, and
+    <code>DeleteWeakGlobalRef</code>.  (The spec strongly encourages
+    programmers to create hard references to weak globals before doing
+    anything with them, so this should not be at all limiting.)</li>
     <li><code>GetObjectRefType</code> (new in 1.6) is implemented but not fully
     functional -- it can't always tell the difference between "local" and
     "global" references.</li>
index bc90527..71073f0 100644 (file)
@@ -472,6 +472,14 @@ static void checkObject(JNIEnv* env, jobject jobj, const char* func)
 
     JNI_ENTER();
 
+    if (dvmIsWeakGlobalRef(jobj)) {
+        /*
+         * Normalize and continue.  This will tell us if the PhantomReference
+         * object is valid.
+         */
+        jobj = dvmNormalizeWeakGlobalRef((jweak) jobj);
+    }
+
     if (dvmGetJNIRefType(env, jobj) == JNIInvalidRefType) {
         LOGW("JNI WARNING: %p is not a valid JNI reference\n", jobj);
         printWarn = true;
index 23b7497..5dd056f 100644 (file)
@@ -187,6 +187,7 @@ struct DvmGlobals {
     ClassObject* classJavaLangReflectMethodArray;
     ClassObject* classJavaLangReflectProxy;
     ClassObject* classJavaLangExceptionInInitializerError;
+    ClassObject* classJavaLangRefPhantomReference;
     ClassObject* classJavaLangRefReference;
     ClassObject* classJavaNioReadWriteDirectByteBuffer;
     ClassObject* classJavaSecurityAccessController;
@@ -279,6 +280,7 @@ struct DvmGlobals {
     /* constructor method pointers; no vtable involved, so use Method* */
     Method*     methJavaLangStackTraceElement_init;
     Method*     methJavaLangExceptionInInitializerError_init;
+    Method*     methJavaLangRefPhantomReference_init;
     Method*     methJavaLangReflectConstructor_init;
     Method*     methJavaLangReflectField_init;
     Method*     methJavaLangReflectMethod_init;
@@ -425,6 +427,9 @@ struct DvmGlobals {
     ReferenceTable  jniPinRefTable;
     pthread_mutex_t jniPinRefLock;
 
+    /* special ReferenceQueue for JNI weak globals */
+    Object*     jniWeakGlobalRefQueue;
+
     /*
      * Native shared library table.
      */
index d7d1122..54a1f45 100644 (file)
--- a/vm/Jni.c
+++ b/vm/Jni.c
@@ -318,12 +318,30 @@ bool dvmJniStartup(void)
 
     dvmInitMutex(&gDvm.jniPinRefLock);
 
+    Method* meth;
+
+    /*
+     * Grab the PhantomReference constructor.
+     */
+    gDvm.classJavaLangRefPhantomReference =
+        dvmFindSystemClassNoInit("Ljava/lang/ref/PhantomReference;");
+    if (gDvm.classJavaLangRefPhantomReference == NULL) {
+        LOGE("Unable to find PhantomReference class\n");
+        return false;
+    }
+    meth= dvmFindDirectMethodByDescriptor(gDvm.classJavaLangRefPhantomReference,
+        "<init>", "(Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V");
+    if (meth == NULL) {
+        LOGE("Unable to find constructor for PhantomReference\n");
+        return false;
+    }
+    gDvm.methJavaLangRefPhantomReference_init = meth;
+
+
     /*
      * Look up and cache pointers to some direct buffer classes, fields,
      * and methods.
      */
-    Method* meth;
-
     ClassObject* platformAddressClass =
         dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
     ClassObject* platformAddressFactoryClass =
@@ -573,6 +591,7 @@ Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
         break;
     case kIndirectKindWeakGlobal:
         {
+            // TODO: implement
             LOGE("weak-global not yet supported\n");
             result = NULL;
             dvmAbort();
@@ -898,6 +917,143 @@ bail:
     dvmUnlockMutex(&gDvm.jniGlobalRefLock);
 }
 
+
+/*
+ * Get the "magic" JNI weak global ReferenceQueue.  It's allocated on
+ * first use.
+ *
+ * Returns NULL with an exception raised if allocation fails.
+ */
+static Object* getWeakGlobalRefQueue(void)
+{
+    /* use an indirect variable to avoid "type-punned pointer" complaints */
+    Object** pGlobalQ = &gDvm.jniWeakGlobalRefQueue;
+
+    if (*pGlobalQ != NULL)
+        return *pGlobalQ;
+
+    ClassObject* clazz = dvmFindSystemClass("Ljava/lang/ref/ReferenceQueue;");
+    if (clazz == NULL) {
+        LOGE("Unable to find java.lang.ref.ReferenceQueue");
+        dvmAbort();
+    }
+
+    /*
+     * Create an instance of ReferenceQueue.  The object is never actually
+     * used for anything, so we don't need to call a constructor.  (We could
+     * get away with using an instance of Object, but this is cleaner.)
+     */
+    Object* queue = dvmAllocObject(clazz, ALLOC_DEFAULT);
+    if (queue == NULL) {
+        LOGW("Failed allocating weak global ref queue\n");
+        assert(dvmCheckException(dvmThreadSelf()));
+        return NULL;
+    }
+    dvmReleaseTrackedAlloc(queue, NULL);
+
+    /*
+     * Save it, using atomic ops to ensure we don't double-up.  The gDvm
+     * field is known to the GC.
+     */
+    if (!ATOMIC_CMP_SWAP((int*) pGlobalQ, 0, (int) queue)) {
+        LOGD("WOW: lost race to create weak global ref queue\n");
+        queue = *pGlobalQ;
+    }
+
+    return queue;
+}
+
+
+/*
+ * We create a PhantomReference that references the object, add a
+ * global reference to it, and then flip some bits before returning it.
+ * The last step ensures that we detect it as special and that only
+ * appropriate calls will accept it.
+ *
+ * On failure, returns NULL with an exception pending.
+ */
+static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj)
+{
+    if (jobj == NULL)
+        return NULL;
+
+    Thread* self = ((JNIEnvExt*)env)->self;
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    Object* weakGlobalQueue = getWeakGlobalRefQueue();
+    Object* phantomObj;
+    jobject phantomRef;
+
+    /*
+     * Allocate a PhantomReference, then call the constructor to set
+     * the referent and the reference queue.
+     *
+     * We use a "magic" reference queue that the GC knows about; it behaves
+     * more like a queueless WeakReference, clearing the referent and
+     * not calling enqueue().
+     */
+    if (!dvmIsClassInitialized(gDvm.classJavaLangRefPhantomReference))
+        dvmInitClass(gDvm.classJavaLangRefPhantomReference);
+    phantomObj = dvmAllocObject(gDvm.classJavaLangRefPhantomReference,
+            ALLOC_DEFAULT);
+    if (phantomObj == NULL) {
+        assert(dvmCheckException(self));
+        LOGW("Failed on WeakGlobalRef alloc\n");
+        return NULL;
+    }
+
+    JValue unused;
+    dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj,
+        &unused, jobj, weakGlobalQueue);
+    dvmReleaseTrackedAlloc(phantomObj, self);
+
+    if (dvmCheckException(self)) {
+        LOGW("PhantomReference init failed\n");
+        return NULL;
+    }
+
+    LOGV("+++ WGR: created phantom ref %p for object %p\n", phantomObj, obj);
+
+    /*
+     * Add it to the global reference table, and mangle the pointer.
+     */
+    phantomRef = addGlobalReference(phantomObj);
+    return dvmObfuscateWeakGlobalRef(phantomRef);
+}
+
+/*
+ * Delete the global reference that's keeping the PhantomReference around.
+ * The PhantomReference will eventually be discarded by the GC.
+ */
+static void deleteWeakGlobalRef(JNIEnv* env, jweak wref)
+{
+    if (wref == NULL)
+        return;
+
+    jobject phantomRef = dvmNormalizeWeakGlobalRef(wref);
+    deleteGlobalReference(phantomRef);
+}
+
+/*
+ * Extract the referent from a PhantomReference.  Used for weak global
+ * references.
+ *
+ * "jwobj" is a "mangled" WGR pointer.
+ */
+static Object* getPhantomReferent(JNIEnv* env, jweak jwobj)
+{
+    jobject jobj = dvmNormalizeWeakGlobalRef(jwobj);
+    Object* obj = dvmDecodeIndirectRef(env, jobj);
+
+    if (obj->clazz != gDvm.classJavaLangRefPhantomReference) {
+        LOGE("%p is not a phantom reference (%s)\n",
+            jwobj, obj->clazz->descriptor);
+        return NULL;
+    }
+
+    return dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent);
+}
+
+
 /*
  * Objects don't currently move, so we just need to create a reference
  * that will ensure the array object isn't collected.
@@ -1121,6 +1277,10 @@ jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
     //Object** top;
     Object** ptr;
 
+    if (dvmIsWeakGlobalRef(jobj)) {
+        return JNIWeakGlobalRefType;
+    }
+
     /* check args */
     if (findInArgList(self, jobj)) {
         //LOGI("--- REF found %p on stack\n", jobj);
@@ -2003,8 +2163,13 @@ static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
  */
 static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
 {
+    Object* obj;
+
     JNI_ENTER();
-    Object* obj = dvmDecodeIndirectRef(env, jobj);
+    if (dvmIsWeakGlobalRef(jobj))
+        obj = getPhantomReferent(env, (jweak) jobj);
+    else
+        obj = dvmDecodeIndirectRef(env, jobj);
     jobject retval = addGlobalReference(obj);
     JNI_EXIT();
     return retval;
@@ -2024,10 +2189,15 @@ static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
 /*
  * Add a reference to the local list.
  */
-static jobject NewLocalRef(JNIEnv* env, jobject jref)
+static jobject NewLocalRef(JNIEnv* env, jobject jobj)
 {
+    Object* obj;
+
     JNI_ENTER();
-    Object* obj = dvmDecodeIndirectRef(env, jref);
+    if (dvmIsWeakGlobalRef(jobj))
+        obj = getPhantomReferent(env, (jweak) jobj);
+    else
+        obj = dvmDecodeIndirectRef(env, jobj);
     jobject retval = addLocalReference(env, obj);
     JNI_EXIT();
     return retval;
@@ -3295,23 +3465,18 @@ static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
 static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
 {
     JNI_ENTER();
-    // TODO - implement
-    jobject gref = NULL;
-    LOGE("JNI ERROR: NewWeakGlobalRef not implemented\n");
-    dvmAbort();
+    jweak wref = createWeakGlobalRef(env, obj);
     JNI_EXIT();
-    return gref;
+    return wref;
 }
 
 /*
  * Delete the specified weak global reference.
  */
-static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
+static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref)
 {
     JNI_ENTER();
-    // TODO - implement
-    LOGE("JNI ERROR: DeleteWeakGlobalRef not implemented\n");
-    dvmAbort();
+    deleteWeakGlobalRef(env, wref);
     JNI_EXIT();
 }
 
index 37920ca..3c15ce4 100644 (file)
@@ -201,4 +201,42 @@ DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo);
  */
 void dvmReleaseJniMonitors(Thread* self);
 
+
+/*
+ * This mask is applied to weak global reference values returned to
+ * native code.  The goal is to create an invalid pointer that will cause
+ * a crash if misused.  The mmap region for the virtual heap is typically
+ * around 0x40xxxxxx.
+ *
+ * To make weak global references easily distinguishable from other kinds
+ * of references when !USE_INDIRECT_REF, we XOR the low bits.  Assuming >=
+ * 64-bit alignment of objects, this changes the low 3 bits from all clear
+ * to all set.
+ */
+#define WEAK_GLOBAL_XOR 0x9e0fffff
+
+/*
+ * "Obfuscate" a weak global reference pointer.
+ */
+INLINE jweak dvmObfuscateWeakGlobalRef(jobject jobj) {
+    return (jweak) ((u4) jobj ^ WEAK_GLOBAL_XOR);
+}
+
+/*
+ * Undo the obfuscation.
+ */
+INLINE jobject dvmNormalizeWeakGlobalRef(jweak ref) {
+    return (jobject) ((u4) ref ^ WEAK_GLOBAL_XOR);
+}
+
+/*
+ * Returns "true" if this looks like a weak global reference.
+ *
+ * Relies on the low 3 bits being set instead of clear (the latter is
+ * guaranteed by 64-bit alignment of objects).
+ */
+INLINE bool dvmIsWeakGlobalRef(jobject jobj) {
+    return (((u4) jobj & 0x07) == 0x07);
+}
+
 #endif /*_DALVIK_JNIINTERNAL*/
index 31832c2..6c14c1c 100644 (file)
@@ -746,7 +746,7 @@ static int findMethodInLib(void* vlib, void* vmethod)
     int len;
 
     if (meth->clazz->classLoader != pLib->classLoader) {
-        LOGD("+++ not scanning '%s' for '%s' (wrong CL)\n",
+        LOGV("+++ not scanning '%s' for '%s' (wrong CL)\n",
             pLib->pathName, meth->name);
         return 0;
     } else
index 4bb917b..634cfda 100644 (file)
@@ -349,6 +349,7 @@ void dvmHeapMarkRootSet()
     dvmMarkObjectNonNull(gDvm.outOfMemoryObj);
     dvmMarkObjectNonNull(gDvm.internalErrorObj);
     dvmMarkObjectNonNull(gDvm.noClassDefFoundErrorObj);
+    dvmMarkObject(gDvm.jniWeakGlobalRefQueue);
 //TODO: scan object references sitting in gDvm;  use pointer begin & end
 
     HPROF_CLEAR_GC_SCAN_STATE();
@@ -861,7 +862,21 @@ size_t numEnqueued = 0;
                  * (The referent will be marked outside of this loop,
                  * after handing all references of this strength, in
                  * case multiple references point to the same object.)
+                 *
+                 * One exception: JNI "weak global" references are handled
+                 * as a special case.  They're identified by the queue.
                  */
+                if (gDvm.jniWeakGlobalRefQueue != NULL) {
+                    Object* queue = dvmGetFieldObject(reference,
+                            gDvm.offJavaLangRefReference_queue);
+                    if (queue == gDvm.jniWeakGlobalRefQueue) {
+                        LOGV("+++ WGR: clearing + not queueing %p:%p\n",
+                            reference, referent);
+                        schedClear = clearReference(reference);
+                        schedEnqueue = false;
+                        break;
+                    }
+                }
                 schedClear = false;
 
                 /* A PhantomReference is only useful with a
@@ -926,6 +941,9 @@ LOGD_HEAP("dvmHeapHandleReferences(): cleared %zd, enqueued %zd %s references\n"
     /* Walk though the reference list again, and mark any non-clear/marked
      * referents.  Only PhantomReferences can have non-clear referents
      * at this point.
+     *
+     * (Could skip this for JNI weak globals, since we know they've been
+     * cleared.)
      */
     if (refType == REF_PHANTOM) {
         bool scanRequired = false;