diff --git a/libjava/ChangeLog b/libjava/ChangeLog index 1e9525aecf9..d12987a7227 100644 --- a/libjava/ChangeLog +++ b/libjava/ChangeLog @@ -1,3 +1,18 @@ +2003-09-29 Tom Tromey + + 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 * java/net/InetAddress.java: diff --git a/libjava/include/jvm.h b/libjava/include/jvm.h index a114e550a15..ed6c61ec50d 100644 --- a/libjava/include/jvm.h +++ b/libjava/include/jvm.h @@ -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. */ diff --git a/libjava/java/lang/natString.cc b/libjava/java/lang/natString.cc index 6fd928478b5..c87844b0d51 100644 --- a/libjava/java/lang/natString.cc +++ b/libjava/java/lang/natString.cc @@ -31,7 +31,6 @@ details. */ #include #include -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 (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; } diff --git a/libjava/java/lang/ref/natReference.cc b/libjava/java/lang/ref/natReference.cc index 551bd0809d1..e322ae37cc8 100644 --- a/libjava/java/lang/ref/natReference.cc +++ b/libjava/java/lang/ref/natReference.cc @@ -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)) { @@ -286,6 +327,23 @@ finalize_reference (jobject ref) _Jv_FinalizeObject (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) {