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);
 | 
						||
    }
 | 
						||
}
 |