mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			305 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			305 lines
		
	
	
		
			8.2 KiB
		
	
	
	
		
			C++
		
	
	
	
| // natReference.cc - Native code for References
 | ||
| 
 | ||
| /* Copyright (C) 2001  Free Software Foundation
 | ||
| 
 | ||
|    This file is part of libgcj.
 | ||
| 
 | ||
| This software is copyrighted work licensed under the terms of the
 | ||
| Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
 | ||
| details.  */
 | ||
| 
 | ||
| // Written by Tom Tromey <tromey@redhat.com>
 | ||
| 
 | ||
| #include <config.h>
 | ||
| 
 | ||
| #include <gcj/cni.h>
 | ||
| #include <jvm.h>
 | ||
| #include <java/lang/Throwable.h>
 | ||
| #include <java/lang/ref/Reference.h>
 | ||
| #include <java/lang/ref/SoftReference.h>
 | ||
| #include <java/lang/ref/WeakReference.h>
 | ||
| #include <java/lang/ref/PhantomReference.h>
 | ||
| #include <java/lang/ref/ReferenceQueue.h>
 | ||
| 
 | ||
| static void finalize_reference (jobject ref);
 | ||
| static void finalize_referred_to_object (jobject obj);
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| enum weight
 | ||
| {
 | ||
|   SOFT = 0,
 | ||
|   WEAK = 1,
 | ||
|   FINALIZE = 2,
 | ||
|   PHANTOM = 3,
 | ||
| 
 | ||
|   // This is used to mark the head of a list.
 | ||
|   HEAD = 4,
 | ||
| 
 | ||
|   // This is used to mark a deleted item.
 | ||
|   DELETED = 5
 | ||
| };
 | ||
| 
 | ||
| // Objects of this type are used in the hash table to keep track of
 | ||
| // the mapping between a finalizable object and the various References
 | ||
| // which refer to it.
 | ||
| struct object_list
 | ||
| {
 | ||
|   // The reference object.  This is NULL for FINALIZE weight.
 | ||
|   jobject reference;
 | ||
| 
 | ||
|   // The weight of this object.
 | ||
|   enum weight weight;
 | ||
| 
 | ||
|   // Next in list.
 | ||
|   object_list *next;
 | ||
| };
 | ||
| 
 | ||
| // Hash table used to hold mapping from object to References.  The
 | ||
| // object_list item in the hash holds the object itself in the
 | ||
| // reference field; chained to it are all the references sorted in
 | ||
| // order of weight (lowest first).
 | ||
| static object_list *hash = NULL;
 | ||
| 
 | ||
| // Number of slots used in HASH.
 | ||
| static int hash_count = 0;
 | ||
| 
 | ||
| // Number of slots total in HASH.  Must be power of 2.
 | ||
| static int hash_size = 0;
 | ||
| 
 | ||
| static object_list *
 | ||
| find_slot (jobject key)
 | ||
| {
 | ||
|   jint hcode = _Jv_HashCode (key);
 | ||
|   /* step must be non-zero, and relatively prime with hash_size. */
 | ||
|   jint step = (hcode ^ (hcode >> 16)) | 1;
 | ||
|   int start_index = hcode & (hash_size - 1);
 | ||
|   int index = start_index;
 | ||
|   int deleted_index = -1;
 | ||
|   for (;;)
 | ||
|     {
 | ||
|       object_list *ptr = &hash[index];
 | ||
|       if (ptr->reference == key)
 | ||
| 	return ptr;
 | ||
|       else if (ptr->reference == NULL)
 | ||
| 	{
 | ||
| 	  if (deleted_index == -1)
 | ||
| 	    return ptr;
 | ||
| 	  else
 | ||
| 	    return &hash[deleted_index];
 | ||
| 	}
 | ||
|       else if (ptr->weight == DELETED)
 | ||
| 	deleted_index = index;
 | ||
|       index = (index + step) & (hash_size - 1);
 | ||
|       JvAssert (index != start_index);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| static void
 | ||
| rehash ()
 | ||
| {
 | ||
|   if (hash == NULL)
 | ||
|     {
 | ||
|       hash_size = 1024;
 | ||
|       hash = (object_list *) _Jv_Malloc (hash_size * sizeof (object_list));
 | ||
|       memset (hash, 0, hash_size * sizeof (object_list));
 | ||
|     }
 | ||
|   else
 | ||
|     {
 | ||
|       object_list *old = hash;
 | ||
|       int i = hash_size;
 | ||
| 
 | ||
|       hash_size *= 2;
 | ||
|       hash = (object_list *) _Jv_Malloc (hash_size * sizeof (object_list));
 | ||
|       memset (hash, 0, hash_size * sizeof (object_list));
 | ||
| 
 | ||
|       while (--i >= 0)
 | ||
| 	{
 | ||
| 	  if (old[i].reference == NULL || old[i].weight == DELETED)
 | ||
| 	    continue;
 | ||
| 	  object_list *newslot = find_slot (old[i].reference);
 | ||
| 	  *newslot = old[i];
 | ||
| 	}
 | ||
| 
 | ||
|       _Jv_Free (old);
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| // Remove a Reference.
 | ||
| static void
 | ||
| remove_from_hash (jobject obj)
 | ||
| {
 | ||
|   java::lang::ref::Reference *ref
 | ||
|     = reinterpret_cast<java::lang::ref::Reference *> (obj);
 | ||
|   object_list *head = find_slot (ref->copy);
 | ||
|   object_list **link = &head->next;
 | ||
|   head = head->next;
 | ||
| 
 | ||
|   while (head && head->reference != ref)
 | ||
|     {
 | ||
|       link = &head->next;
 | ||
|       head = head->next;
 | ||
|     }
 | ||
| 
 | ||
|   // Remove the slot.
 | ||
|   if (head)
 | ||
|     {
 | ||
|       *link = head->next;
 | ||
|       _Jv_Free (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!
 | ||
| 
 | ||
| // Add an item to the hash table.  If the item is new, we also add a
 | ||
| // finalizer item.  We keep items in the hash table until they are
 | ||
| // completely collected; this lets us know when an item is new, even
 | ||
| // if it has been resurrected after its finalizer has been run.
 | ||
| static void
 | ||
| add_to_hash (java::lang::ref::Reference *the_reference)
 | ||
| {
 | ||
|   JvSynchronize sync (java::lang::ref::Reference::lock);
 | ||
| 
 | ||
|   if (3 * hash_count >= 2 * hash_size)
 | ||
|     rehash ();
 | ||
| 
 | ||
|   jobject referent = the_reference->referent;
 | ||
|   object_list *item = find_slot (referent);
 | ||
|   if (item->reference == NULL)
 | ||
|     {
 | ||
|       // New item, so make an entry for the finalizer.
 | ||
|       item->reference = referent;
 | ||
|       item->weight = HEAD;
 | ||
| 
 | ||
|       item->next = (object_list *) _Jv_Malloc (sizeof (object_list));
 | ||
|       item->next->reference = NULL;
 | ||
|       item->next->weight = FINALIZE;
 | ||
|       item->next->next = NULL;
 | ||
|       ++hash_count;
 | ||
|     }
 | ||
| 
 | ||
|   object_list *n = (object_list *) _Jv_Malloc (sizeof (object_list));
 | ||
|   n->reference = the_reference;
 | ||
| 
 | ||
|   enum weight w = PHANTOM;
 | ||
|   if (java::lang::ref::SoftReference::class$.isInstance (the_reference))
 | ||
|     w = SOFT;
 | ||
|   else if (java::lang::ref::WeakReference::class$.isInstance (the_reference))
 | ||
|     w = WEAK;
 | ||
|   n->weight = w;
 | ||
| 
 | ||
|   object_list **link = &item->next;
 | ||
|   object_list *iter = *link;
 | ||
|   while (iter && iter->weight < n->weight)
 | ||
|     {
 | ||
|       link = &iter->next;
 | ||
|       iter = *link;
 | ||
|     }
 | ||
|   n->next = (*link) ? (*link)->next : NULL;
 | ||
|   *link = n;
 | ||
| }
 | ||
| 
 | ||
| // This is called when an object is ready to be finalized.  This
 | ||
| // actually implements the appropriate Reference semantics.
 | ||
| static void
 | ||
| finalize_referred_to_object (jobject obj)
 | ||
| {
 | ||
|   JvSynchronize sync (java::lang::ref::Reference::lock);
 | ||
| 
 | ||
|   object_list *list = find_slot (obj);
 | ||
|   object_list *head = list->next;
 | ||
|   if (head == NULL)
 | ||
|     {
 | ||
|       // We have a truly dead object: the object's finalizer has been
 | ||
|       // run, all the object's references have been processed, and the
 | ||
|       // object is unreachable.  There is, at long last, no way to
 | ||
|       // resurrect it.
 | ||
|       list->weight = DELETED;
 | ||
|       --hash_count;
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|   enum weight w = head->weight;
 | ||
|   if (w == FINALIZE)
 | ||
|     {
 | ||
|       // 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
 | ||
| 	_Jv_FinalizeObject (obj);
 | ||
|       list->next = head->next;
 | ||
|       _Jv_Free (head);
 | ||
|     }
 | ||
|   else if (w != SOFT || _Jv_GCCanReclaimSoftReference (obj))
 | ||
|     {
 | ||
|       // If we just decided to reclaim a soft reference, we might as
 | ||
|       // well do all the weak references at the same time.
 | ||
|       if (w == SOFT)
 | ||
| 	w = WEAK;
 | ||
| 
 | ||
|       while (head && head->weight <= w)
 | ||
| 	{
 | ||
| 	  java::lang::ref::Reference *ref
 | ||
| 	    = reinterpret_cast<java::lang::ref::Reference *> (head->reference);
 | ||
| 	  // If the copy is already NULL then the user must have
 | ||
| 	  // called Reference.clear().
 | ||
| 	  if (ref->copy != NULL)
 | ||
| 	    {
 | ||
| 	      if (w == PHANTOM)
 | ||
| 		ref->referent = ref->copy;
 | ||
| 	      else
 | ||
| 		ref->copy = NULL;
 | ||
| 	      ref->enqueue ();
 | ||
| 	    }
 | ||
| 
 | ||
| 	  object_list *next = head->next;
 | ||
| 	  _Jv_Free (head);
 | ||
| 	  head = next;
 | ||
| 	}
 | ||
|       list->next = head;
 | ||
|     }
 | ||
| 
 | ||
|   // Re-register this finalizer.  We always re-register because we
 | ||
|   // can't know until the next collection cycle whether or not the
 | ||
|   // object is truly unreachable.
 | ||
|   _Jv_RegisterFinalizer (obj, finalize_referred_to_object);
 | ||
| }
 | ||
| 
 | ||
| // This is called when a Reference object is finalized.  If there is a
 | ||
| // Reference pointing to this Reference then that case is handled by
 | ||
| // finalize_referred_to_object.
 | ||
| static void
 | ||
| finalize_reference (jobject ref)
 | ||
| {
 | ||
|   JvSynchronize sync (java::lang::ref::Reference::lock);
 | ||
|   remove_from_hash (ref);
 | ||
|   // The user might have a subclass of Reference with a finalizer.
 | ||
|   _Jv_FinalizeObject (ref);
 | ||
| }
 | ||
| 
 | ||
| void
 | ||
| ::java::lang::ref::Reference::create (jobject ref)
 | ||
| {
 | ||
|   // Nothing says you can't make a Reference with a NULL referent.
 | ||
|   // But there's nothing to do in such a case.
 | ||
|   referent = reinterpret_cast<gnu::gcj::RawData *> (ref);
 | ||
|   copy = referent;
 | ||
|   if (referent != NULL)
 | ||
|     {
 | ||
|       JvSynchronize sync (java::lang::ref::Reference::lock);
 | ||
|       // `this' is a new Reference object.  We register a new
 | ||
|       // finalizer for pointed-to object and we arrange a special
 | ||
|       // finalizer for ourselves as well.
 | ||
|       _Jv_RegisterFinalizer (this, finalize_reference);
 | ||
|       _Jv_RegisterFinalizer (referent, finalize_referred_to_object);
 | ||
|       jobject *objp = reinterpret_cast<jobject *> (&referent);
 | ||
|       _Jv_GCRegisterDisappearingLink (objp);
 | ||
|       add_to_hash (this);
 | ||
|     }
 | ||
| }
 |