2001-10-02 22:31:47 +08:00
|
|
|
|
// 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;
|
2001-10-04 00:47:02 +08:00
|
|
|
|
*link = n;
|
2001-10-02 22:31:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
}
|