#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,
HEAD = 4,
DELETED = 5
};
struct object_list
{
jobject reference;
enum weight weight;
object_list *next;
};
static object_list *hash = NULL;
static int hash_count = 0;
static int hash_size = 0;
static object_list *
find_slot (jobject key)
{
jint hcode = _Jv_HashCode (key);
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);
}
}
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;
}
if (head)
{
*link = head->next;
_Jv_Free (head);
}
}
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->copy;
object_list *item = find_slot (referent);
if (item->reference == NULL)
{
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 = n;
}
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)
{
list->weight = DELETED;
--hash_count;
return;
}
enum weight w = head->weight;
if (w == FINALIZE)
{
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 (w == SOFT)
w = WEAK;
while (head && head->weight <= w)
{
java::lang::ref::Reference *ref
= reinterpret_cast<java::lang::ref::Reference *> (head->reference);
if (ref->copy != NULL)
ref->enqueue ();
object_list *next = head->next;
_Jv_Free (head);
head = next;
}
list->next = head;
}
_Jv_RegisterFinalizer (obj, finalize_referred_to_object);
}
static void
finalize_reference (jobject ref)
{
JvSynchronize sync (java::lang::ref::Reference::lock);
remove_from_hash (ref);
_Jv_FinalizeObject (ref);
}
void
::java::lang::ref::Reference::create (jobject ref)
{
referent = reinterpret_cast<gnu::gcj::RawData *> (ref);
copy = referent;
if (referent != NULL)
{
JvSynchronize sync (java::lang::ref::Reference::lock);
_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);
}
}