gcc/libjava/java/lang/ref/natReference.cc

305 lines
8.2 KiB
C++
Raw Normal View History

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