2003-12-26 Guilhem Lavaux <guilhem@kaffe.org>
[official-gcc.git] / libjava / java / lang / ref / natReference.cc
blobe322ae37cc820aac675399a3fb90ba0d8ac7a428
1 // natReference.cc - Native code for References
3 /* Copyright (C) 2001, 2002, 2003 Free Software Foundation
5 This file is part of libgcj.
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
9 details. */
11 // Written by Tom Tromey <tromey@redhat.com>
13 #include <config.h>
15 #include <gcj/cni.h>
16 #include <jvm.h>
17 #include <java/lang/Throwable.h>
18 #include <java/lang/ref/Reference.h>
19 #include <java/lang/ref/SoftReference.h>
20 #include <java/lang/ref/WeakReference.h>
21 #include <java/lang/ref/PhantomReference.h>
22 #include <java/lang/ref/ReferenceQueue.h>
24 static void finalize_reference (jobject ref);
25 static void finalize_referred_to_object (jobject obj);
29 enum weight
31 SOFT = 0,
32 WEAK = 1,
33 FINALIZE = 2,
34 PHANTOM = 3,
36 // This is used to mark the head of a list.
37 HEAD = 4,
39 // This is used to mark a deleted item.
40 DELETED = 5
43 // Objects of this type are used in the hash table to keep track of
44 // the mapping between a finalizable object and the various References
45 // which refer to it.
46 struct object_list
48 // The reference object. This is NULL for FINALIZE weight.
49 jobject reference;
51 // The weight of this object.
52 enum weight weight;
54 // Next in list.
55 object_list *next;
58 // Hash table used to hold mapping from object to References. The
59 // object_list item in the hash holds the object itself in the
60 // reference field; chained to it are all the references sorted in
61 // order of weight (lowest first).
62 static object_list *hash = NULL;
64 // Number of slots used in HASH.
65 static int hash_count = 0;
67 // Number of slots total in HASH. Must be power of 2.
68 static int hash_size = 0;
70 #define DELETED_REFERENCE ((jobject) -1)
72 static object_list *
73 find_slot (jobject key)
75 jint hcode = _Jv_HashCode (key);
76 /* step must be non-zero, and relatively prime with hash_size. */
77 jint step = (hcode ^ (hcode >> 16)) | 1;
78 int start_index = hcode & (hash_size - 1);
79 int index = start_index;
80 int deleted_index = -1;
81 for (;;)
83 object_list *ptr = &hash[index];
84 if (ptr->reference == key)
85 return ptr;
86 else if (ptr->reference == NULL)
88 if (deleted_index == -1)
89 return ptr;
90 else
91 return &hash[deleted_index];
93 else if (ptr->weight == DELETED)
95 deleted_index = index;
96 JvAssert (ptr->reference == DELETED_REFERENCE);
98 index = (index + step) & (hash_size - 1);
99 JvAssert (index != start_index);
103 static void
104 rehash ()
106 if (hash == NULL)
108 hash_size = 1024;
109 hash = (object_list *) _Jv_Malloc (hash_size * sizeof (object_list));
110 memset (hash, 0, hash_size * sizeof (object_list));
112 else
114 object_list *old = hash;
115 int i = hash_size;
117 hash_size *= 2;
118 hash = (object_list *) _Jv_Malloc (hash_size * sizeof (object_list));
119 memset (hash, 0, hash_size * sizeof (object_list));
121 while (--i >= 0)
123 if (old[i].reference == NULL || old[i].weight == DELETED)
124 continue;
125 object_list *newslot = find_slot (old[i].reference);
126 *newslot = old[i];
129 _Jv_Free (old);
133 // Remove a Reference.
134 static void
135 remove_from_hash (jobject obj)
137 java::lang::ref::Reference *ref
138 = reinterpret_cast<java::lang::ref::Reference *> (obj);
139 object_list *head = find_slot (ref->copy);
141 // We might have found a new slot. We can just ignore that here.
142 if (head->reference != ref->copy)
143 return;
145 object_list **link = &head->next;
146 head = head->next;
148 while (head && head->reference != ref)
150 link = &head->next;
151 head = head->next;
154 // Remove the slot.
155 if (head)
157 *link = head->next;
158 _Jv_Free (head);
162 // Return list head if object is in hash, NULL otherwise.
163 object_list *
164 in_hash (jobject obj)
166 // The hash table might not yet be initialized.
167 if (hash == NULL)
168 return NULL;
169 object_list *head = find_slot (obj);
170 if (head->reference != obj)
171 return NULL;
172 return head;
175 // FIXME what happens if an object's finalizer creates a Reference to
176 // the object, and the object has never before been added to the hash?
177 // Madness!
179 // Add an item to the hash table. If the item is new, we also add a
180 // finalizer item. We keep items in the hash table until they are
181 // completely collected; this lets us know when an item is new, even
182 // if it has been resurrected after its finalizer has been run.
183 static void
184 add_to_hash (java::lang::ref::Reference *the_reference)
186 JvSynchronize sync (java::lang::ref::Reference::lock);
188 if (3 * hash_count >= 2 * hash_size)
189 rehash ();
191 // Use `copy' here because the `referent' field has been cleared.
192 jobject referent = the_reference->copy;
193 object_list *item = find_slot (referent);
194 if (item->reference == NULL || item->reference == DELETED_REFERENCE)
196 // New item, so make an entry for the finalizer.
197 item->reference = referent;
198 item->weight = HEAD;
200 item->next = (object_list *) _Jv_Malloc (sizeof (object_list));
201 item->next->reference = NULL;
202 item->next->weight = FINALIZE;
203 item->next->next = NULL;
204 ++hash_count;
207 object_list *n = (object_list *) _Jv_Malloc (sizeof (object_list));
208 n->reference = the_reference;
210 enum weight w = PHANTOM;
211 if (java::lang::ref::SoftReference::class$.isInstance (the_reference))
212 w = SOFT;
213 else if (java::lang::ref::WeakReference::class$.isInstance (the_reference))
214 w = WEAK;
215 n->weight = w;
217 object_list **link = &item->next;
218 object_list *iter = *link;
219 while (iter && iter->weight < n->weight)
221 link = &iter->next;
222 iter = *link;
224 n->next = *link;
225 *link = n;
228 // Add a FINALIZE entry if one doesn't exist.
229 static void
230 maybe_add_finalize (object_list *entry, jobject obj)
232 object_list **link = &entry->next;
233 object_list *iter = *link;
234 while (iter && iter->weight < FINALIZE)
236 link = &iter->next;
237 iter = *link;
240 // We want at most one FINALIZE entry in the queue.
241 if (iter && iter->weight == FINALIZE)
242 return;
244 object_list *n = (object_list *) _Jv_Malloc (sizeof (object_list));
245 n->reference = obj;
246 n->weight = FINALIZE;
247 n->next = *link;
248 *link = n;
251 // This is called when an object is ready to be finalized. This
252 // actually implements the appropriate Reference semantics.
253 static void
254 finalize_referred_to_object (jobject obj)
256 JvSynchronize sync (java::lang::ref::Reference::lock);
258 object_list *list = find_slot (obj);
259 object_list *head = list->next;
260 if (head == NULL)
262 // We have a truly dead object: the object's finalizer has been
263 // run, all the object's references have been processed, and the
264 // object is unreachable. There is, at long last, no way to
265 // resurrect it.
266 list->reference = DELETED_REFERENCE;
267 list->weight = DELETED;
268 --hash_count;
269 return;
272 enum weight w = head->weight;
273 if (w == FINALIZE)
275 // Update the list first, as _Jv_FinalizeString might end up
276 // looking at this data structure.
277 list->next = head->next;
278 _Jv_Free (head);
280 // If we have a Reference A to a Reference B, and B is
281 // finalized, then we have to take special care to make sure
282 // that B is properly deregistered. This is super gross. FIXME
283 // will it fail if B's finalizer resurrects B?
284 if (java::lang::ref::Reference::class$.isInstance (obj))
285 finalize_reference (obj);
286 else if (obj->getClass() == &java::lang::String::class$)
287 _Jv_FinalizeString (obj);
288 else
289 _Jv_FinalizeObject (obj);
291 else if (w != SOFT || _Jv_GCCanReclaimSoftReference (obj))
293 // If we just decided to reclaim a soft reference, we might as
294 // well do all the weak references at the same time.
295 if (w == SOFT)
296 w = WEAK;
298 while (head && head->weight <= w)
300 java::lang::ref::Reference *ref
301 = reinterpret_cast<java::lang::ref::Reference *> (head->reference);
302 if (! ref->cleared)
303 ref->enqueue ();
305 object_list *next = head->next;
306 _Jv_Free (head);
307 head = next;
309 list->next = head;
312 // Re-register this finalizer. We always re-register because we
313 // can't know until the next collection cycle whether or not the
314 // object is truly unreachable.
315 _Jv_RegisterFinalizer (obj, finalize_referred_to_object);
318 // This is called when a Reference object is finalized. If there is a
319 // Reference pointing to this Reference then that case is handled by
320 // finalize_referred_to_object.
321 static void
322 finalize_reference (jobject ref)
324 JvSynchronize sync (java::lang::ref::Reference::lock);
325 remove_from_hash (ref);
326 // The user might have a subclass of Reference with a finalizer.
327 _Jv_FinalizeObject (ref);
330 void
331 _Jv_RegisterStringFinalizer (jobject str)
333 // This function might be called before any other Reference method,
334 // so we must ensure the class is initialized.
335 _Jv_InitClass (&java::lang::ref::Reference::class$);
336 JvSynchronize sync (java::lang::ref::Reference::lock);
337 // If the object is in our hash table, then we might need to add a
338 // new FINALIZE entry. Otherwise, we just register an ordinary
339 // finalizer.
340 object_list *entry = in_hash (str);
341 if (entry)
342 maybe_add_finalize (entry, str);
343 else
344 _Jv_RegisterFinalizer ((void *) str, _Jv_FinalizeString);
347 void
348 ::java::lang::ref::Reference::create (jobject ref)
350 // Nothing says you can't make a Reference with a NULL referent.
351 // But there's nothing to do in such a case.
352 referent = reinterpret_cast<gnu::gcj::RawData *> (ref);
353 copy = referent;
354 if (referent != NULL)
356 JvSynchronize sync (java::lang::ref::Reference::lock);
357 // `this' is a new Reference object. We register a new
358 // finalizer for pointed-to object and we arrange a special
359 // finalizer for ourselves as well.
360 _Jv_RegisterFinalizer (this, finalize_reference);
361 _Jv_RegisterFinalizer (referent, finalize_referred_to_object);
362 jobject *objp = reinterpret_cast<jobject *> (&referent);
363 _Jv_GCRegisterDisappearingLink (objp);
364 add_to_hash (this);