re PR libgcj/10596 (Reference and String.intern don't work together)
authorTom Tromey <tromey@redhat.com>
Mon, 29 Sep 2003 21:13:55 +0000 (21:13 +0000)
committerTom Tromey <tromey@gcc.gnu.org>
Mon, 29 Sep 2003 21:13:55 +0000 (21:13 +0000)
PR libgcj/10596:
* include/jvm.h (_Jv_FinalizeString,
_Jv_RegisterStringFinalizer): Declare.
* java/lang/natString.cc (_Jv_FinalizeString): Renamed from
unintern.
(intern): Updated.
(_Jv_NewStringUtf8Const): Likewise.
* java/lang/ref/natReference.cc (finalize_referred_to_object):
Add special case when finalizing a String.
(in_hash): New function.
(_Jv_RegisterStringFinalizer): Likewise.
(maybe_add_finalize): Likewise.

From-SVN: r71915

libjava/ChangeLog
libjava/include/jvm.h
libjava/java/lang/natString.cc
libjava/java/lang/ref/natReference.cc

index 1e9525a..d12987a 100644 (file)
@@ -1,3 +1,18 @@
+2003-09-29  Tom Tromey  <tromey@redhat.com>
+
+       PR libgcj/10596:
+       * include/jvm.h (_Jv_FinalizeString,
+       _Jv_RegisterStringFinalizer): Declare.
+       * java/lang/natString.cc (_Jv_FinalizeString): Renamed from
+       unintern.
+       (intern): Updated.
+       (_Jv_NewStringUtf8Const): Likewise.
+       * java/lang/ref/natReference.cc (finalize_referred_to_object):
+       Add special case when finalizing a String.
+       (in_hash): New function.
+       (_Jv_RegisterStringFinalizer): Likewise.
+       (maybe_add_finalize): Likewise.
+
 2003-09-29  Michael Koch  <konqueror@gmx.de>
 
        * java/net/InetAddress.java:
index a114e55..ed6c61e 100644 (file)
@@ -290,6 +290,12 @@ void _Jv_GCRegisterDisappearingLink (jobject *objp);
    implement soft references.  */
 jboolean _Jv_GCCanReclaimSoftReference (jobject obj);
 
+/* Register a finalizer for a String object.  This is only used by
+   the intern() implementation.  */
+void _Jv_RegisterStringFinalizer (jobject str);
+/* This is called to actually finalize a possibly-intern()d String.  */
+void _Jv_FinalizeString (jobject str);
+
 /* Return approximation of total size of heap.  */
 long _Jv_GCTotalMemory (void);
 /* Return approximation of total free memory.  */
index 6fd9284..c87844b 100644 (file)
@@ -31,7 +31,6 @@ details.  */
 #include <gnu/gcj/runtime/StringBuffer.h>
 #include <jvm.h>
 
-static void unintern (jobject);
 static jstring* strhash = NULL;
 static int strhash_count = 0;  /* Number of slots used in strhash. */
 static int strhash_size = 0;  /* Number of slots available in strhash.
@@ -173,28 +172,38 @@ java::lang::String::intern()
   jstring* ptr = _Jv_StringGetSlot(this);
   if (*ptr != NULL && *ptr != DELETED_STRING)
     {
-      // See description in unintern() to understand this.
+      // See description in _Jv_FinalizeString() to understand this.
       *ptr = (jstring) MASK_PTR (*ptr);
       return (jstring) UNMASK_PTR (*ptr);
     }
-  jstring str = this->data == this ? this
-    : _Jv_NewString(JvGetStringChars(this), this->length());
+  jstring str = (this->data == this
+                ? this
+                : _Jv_NewString(JvGetStringChars(this), this->length()));
   SET_STRING_IS_INTERNED(str);
   strhash_count++;
   *ptr = str;
   // When string is GC'd, clear the slot in the hash table.
-  _Jv_RegisterFinalizer ((void *) str, unintern);
+  _Jv_RegisterStringFinalizer (str);
   return str;
 }
 
-/* Called by String fake finalizer. */
-static void
-unintern (jobject obj)
+// The fake String finalizer.  This is only used when the String has
+// been intern()d.  However, we must check this case, as it might be
+// called by the Reference code for any String.
+void
+_Jv_FinalizeString (jobject obj)
 {
   JvSynchronize sync (&StringClass);
+
+  // We might not actually have intern()d any strings at all, if
+  // we're being called from Reference.
+  if (! strhash)
+    return;
+
   jstring str = reinterpret_cast<jstring> (obj);
-  jstring* ptr = _Jv_StringGetSlot(str);
-  if (*ptr == NULL || *ptr == DELETED_STRING)
+  jstring *ptr = _Jv_StringGetSlot(str);
+  if (*ptr == NULL || *ptr == DELETED_STRING
+      || (jobject) UNMASK_PTR (*ptr) != obj)
     return;
 
   // We assume the lowest bit of the pointer is free for our nefarious
@@ -202,21 +211,21 @@ unintern (jobject obj)
   // interning the String.  If we subsequently re-intern the same
   // String, then we set the bit.  When finalizing, if the bit is set
   // then we clear it and re-register the finalizer.  We know this is
-  // a safe approach because both intern() and unintern() acquire
-  // the class lock; this bit can't be manipulated when the lock is
-  // not held.  So if we are finalizing and the bit is clear then we
-  // know all references are gone and we can clear the entry in the
-  // hash table.  The naive approach of simply clearing the pointer
-  // here fails in the case where a request to intern a new string
-  // with the same contents is made between the time the intern()d
-  // string is found to be unreachable and when the finalizer is
-  // actually run.  In this case we could clear a pointer to a valid
-  // string, and future intern() calls for that particular value would
-  // spuriously fail.
+  // a safe approach because both intern() and _Jv_FinalizeString()
+  // acquire the class lock; this bit can't be manipulated when the
+  // lock is not held.  So if we are finalizing and the bit is clear
+  // then we know all references are gone and we can clear the entry
+  // in the hash table.  The naive approach of simply clearing the
+  // pointer here fails in the case where a request to intern a new
+  // string with the same contents is made between the time the
+  // intern()d string is found to be unreachable and when the
+  // finalizer is actually run.  In this case we could clear a pointer
+  // to a valid string, and future intern() calls for that particular
+  // value would spuriously fail.
   if (PTR_MASKED (*ptr))
     {
       *ptr = (jstring) UNMASK_PTR (*ptr);
-      _Jv_RegisterFinalizer ((void *) obj, unintern);
+      _Jv_RegisterStringFinalizer (obj);
     }
   else
     {
@@ -292,8 +301,10 @@ _Jv_NewStringUtf8Const (Utf8Const* str)
   jstr->cachedHashCode = hash;
   *ptr = jstr;
   SET_STRING_IS_INTERNED(jstr);
-  // When string is GC'd, clear the slot in the hash table.
-  _Jv_RegisterFinalizer ((void *) jstr, unintern);
+  // When string is GC'd, clear the slot in the hash table.  Note that
+  // we don't have to call _Jv_RegisterStringFinalizer here, as we
+  // know the new object cannot be referred to by a Reference.
+  _Jv_RegisterFinalizer ((void *) jstr, _Jv_FinalizeString);
   return jstr;
 }
 
index 551bd08..e322ae3 100644 (file)
@@ -159,6 +159,19 @@ remove_from_hash (jobject obj)
     }
 }
 
+// Return list head if object is in hash, NULL otherwise.
+object_list *
+in_hash (jobject obj)
+{
+  // The hash table might not yet be initialized.
+  if (hash == NULL)
+    return NULL;
+  object_list *head = find_slot (obj);
+  if (head->reference != obj)
+    return NULL;
+  return head;
+}
+
 // FIXME what happens if an object's finalizer creates a Reference to
 // the object, and the object has never before been added to the hash?
 // Madness!
@@ -212,6 +225,29 @@ add_to_hash (java::lang::ref::Reference *the_reference)
   *link = n;
 }
 
+// Add a FINALIZE entry if one doesn't exist.
+static void
+maybe_add_finalize (object_list *entry, jobject obj)
+{
+  object_list **link = &entry->next;
+  object_list *iter = *link;
+  while (iter && iter->weight < FINALIZE)
+    {
+      link = &iter->next;
+      iter = *link;
+    }
+
+  // We want at most one FINALIZE entry in the queue.
+  if (iter && iter->weight == FINALIZE)
+    return;
+
+  object_list *n = (object_list *) _Jv_Malloc (sizeof (object_list));
+  n->reference = obj;
+  n->weight = FINALIZE;
+  n->next = *link;
+  *link = n;
+}
+
 // This is called when an object is ready to be finalized.  This
 // actually implements the appropriate Reference semantics.
 static void
@@ -236,16 +272,21 @@ finalize_referred_to_object (jobject obj)
   enum weight w = head->weight;
   if (w == FINALIZE)
     {
+      // Update the list first, as _Jv_FinalizeString might end up
+      // looking at this data structure.
+      list->next = head->next;
+      _Jv_Free (head);
+
       // If we have a Reference A to a Reference B, and B is
       // finalized, then we have to take special care to make sure
       // that B is properly deregistered.  This is super gross.  FIXME
       // will it fail if B's finalizer resurrects B?
       if (java::lang::ref::Reference::class$.isInstance (obj))
        finalize_reference (obj);
+      else if (obj->getClass() == &java::lang::String::class$)
+       _Jv_FinalizeString (obj);
       else
        _Jv_FinalizeObject (obj);
-      list->next = head->next;
-      _Jv_Free (head);
     }
   else if (w != SOFT || _Jv_GCCanReclaimSoftReference (obj))
     {
@@ -287,6 +328,23 @@ finalize_reference (jobject ref)
 }
 
 void
+_Jv_RegisterStringFinalizer (jobject str)
+{
+  // This function might be called before any other Reference method,
+  // so we must ensure the class is initialized.
+  _Jv_InitClass (&java::lang::ref::Reference::class$);
+  JvSynchronize sync (java::lang::ref::Reference::lock);
+  // If the object is in our hash table, then we might need to add a
+  // new FINALIZE entry.  Otherwise, we just register an ordinary
+  // finalizer.
+  object_list *entry = in_hash (str);
+  if (entry)
+    maybe_add_finalize (entry, str);
+  else
+    _Jv_RegisterFinalizer ((void *) str, _Jv_FinalizeString);
+}
+
+void
 ::java::lang::ref::Reference::create (jobject ref)
 {
   // Nothing says you can't make a Reference with a NULL referent.