OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / java / lang / ref / natReference.cc
1 // natReference.cc - Native code for References
2
3 /* Copyright (C) 2001, 2002, 2003, 2005, 2006  Free Software Foundation
4
5    This file is part of libgcj.
6
7 This software is copyrighted work licensed under the terms of the
8 Libgcj License.  Please consult the file "LIBGCJ_LICENSE" for
9 details.  */
10
11 // Written by Tom Tromey <tromey@redhat.com>
12
13 #include <config.h>
14
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>
23
24 static void finalize_reference (jobject ref);
25 static void finalize_referred_to_object (jobject obj);
26
27 \f
28
29 enum weight
30 {
31   SOFT = 0,
32   WEAK = 1,
33   FINALIZE = 2,
34   PHANTOM = 3,
35
36   // This is used to mark the head of a list.
37   HEAD = 4,
38
39   // This is used to mark a deleted item.
40   DELETED = 5
41 };
42
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
47 {
48   // The reference object.  This is NULL for FINALIZE weight.
49   jobject reference;
50
51   // The weight of this object.
52   enum weight weight;
53
54   // Next in list.
55   object_list *next;
56 };
57
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;
63
64 // Number of slots used in HASH.
65 static int hash_count = 0;
66
67 // Number of slots total in HASH.  Must be power of 2.
68 static int hash_size = 0;
69
70 #define DELETED_REFERENCE  ((jobject) -1)
71
72 static object_list *
73 find_slot (jobject key)
74 {
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   do
82     {
83       object_list *ptr = &hash[index];
84       if (ptr->reference == key)
85         return ptr;
86       else if (ptr->reference == NULL)
87         {
88           if (deleted_index == -1)
89             return ptr;
90           else
91             return &hash[deleted_index];
92         }
93       else if (ptr->weight == DELETED)
94         {
95           deleted_index = index;
96           JvAssert (ptr->reference == DELETED_REFERENCE);
97         }
98       index = (index + step) & (hash_size - 1);
99     }
100   while (index != start_index);
101   // Note that we can have INDEX == START_INDEX if the table has no
102   // NULL entries but does have DELETED entries.
103   JvAssert (deleted_index >= 0);
104   return &hash[deleted_index];
105 }
106
107 static void
108 rehash ()
109 {
110   if (hash == NULL)
111     {
112       hash_size = 1024;
113       hash = (object_list *) _Jv_Malloc (hash_size * sizeof (object_list));
114       memset (hash, 0, hash_size * sizeof (object_list));
115     }
116   else
117     {
118       object_list *old = hash;
119       int i = hash_size;
120
121       hash_size *= 2;
122       hash = (object_list *) _Jv_Malloc (hash_size * sizeof (object_list));
123       memset (hash, 0, hash_size * sizeof (object_list));
124
125       while (--i >= 0)
126         {
127           if (old[i].reference == NULL || old[i].weight == DELETED)
128             continue;
129           object_list *newslot = find_slot (old[i].reference);
130           *newslot = old[i];
131         }
132
133       _Jv_Free (old);
134     }
135 }
136
137 // Remove a Reference.
138 static void
139 remove_from_hash (jobject obj)
140 {
141   java::lang::ref::Reference *ref
142     = reinterpret_cast<java::lang::ref::Reference *> (obj);
143   object_list *head = find_slot (ref->copy);
144
145   // We might have found a new slot.  We can just ignore that here.
146   if (head->reference != ref->copy)
147     return;
148
149   object_list **link = &head->next;
150   head = head->next;
151
152   while (head && head->reference != ref)
153     {
154       link = &head->next;
155       head = head->next;
156     }
157
158   // Remove the slot.
159   if (head)
160     {
161       *link = head->next;
162       _Jv_Free (head);
163     }
164 }
165
166 // Return list head if object is in hash, NULL otherwise.
167 object_list *
168 in_hash (jobject obj)
169 {
170   // The hash table might not yet be initialized.
171   if (hash == NULL)
172     return NULL;
173   object_list *head = find_slot (obj);
174   if (head->reference != obj)
175     return NULL;
176   return head;
177 }
178
179 // FIXME what happens if an object's finalizer creates a Reference to
180 // the object, and the object has never before been added to the hash?
181 // Madness!
182
183 // Add an item to the hash table.  If the item is new, we also add a
184 // finalizer item.  We keep items in the hash table until they are
185 // completely collected; this lets us know when an item is new, even
186 // if it has been resurrected after its finalizer has been run.
187 static void
188 add_to_hash (java::lang::ref::Reference *the_reference)
189 {
190   JvSynchronize sync (java::lang::ref::Reference::lock);
191
192   if (3 * hash_count >= 2 * hash_size)
193     rehash ();
194
195   // Use `copy' here because the `referent' field has been cleared.
196   jobject referent = the_reference->copy;
197   object_list *item = find_slot (referent);
198   if (item->reference == NULL || item->reference == DELETED_REFERENCE)
199     {
200       // New item, so make an entry for the finalizer.
201       item->reference = referent;
202       item->weight = HEAD;
203
204       item->next = (object_list *) _Jv_Malloc (sizeof (object_list));
205       item->next->reference = NULL;
206       item->next->weight = FINALIZE;
207       item->next->next = NULL;
208       ++hash_count;
209     }
210
211   object_list *n = (object_list *) _Jv_Malloc (sizeof (object_list));
212   n->reference = the_reference;
213
214   enum weight w = PHANTOM;
215   if (java::lang::ref::SoftReference::class$.isInstance (the_reference))
216     w = SOFT;
217   else if (java::lang::ref::WeakReference::class$.isInstance (the_reference))
218     w = WEAK;
219   n->weight = w;
220
221   object_list **link = &item->next;
222   object_list *iter = *link;
223   while (iter && iter->weight < n->weight)
224     {
225       link = &iter->next;
226       iter = *link;
227     }
228   n->next = *link;
229   *link = n;
230 }
231
232 // Add a FINALIZE entry if one doesn't exist.
233 static void
234 maybe_add_finalize (object_list *entry, jobject obj)
235 {
236   object_list **link = &entry->next;
237   object_list *iter = *link;
238   while (iter && iter->weight < FINALIZE)
239     {
240       link = &iter->next;
241       iter = *link;
242     }
243
244   // We want at most one FINALIZE entry in the queue.
245   if (iter && iter->weight == FINALIZE)
246     return;
247
248   object_list *n = (object_list *) _Jv_Malloc (sizeof (object_list));
249   n->reference = obj;
250   n->weight = FINALIZE;
251   n->next = *link;
252   *link = n;
253 }
254
255 // This is called when an object is ready to be finalized.  This
256 // actually implements the appropriate Reference semantics.
257 static void
258 finalize_referred_to_object (jobject obj)
259 {
260   JvSynchronize sync (java::lang::ref::Reference::lock);
261
262   object_list *list = find_slot (obj);
263   object_list *head = list->next;
264   if (head == NULL)
265     {
266       // We have a truly dead object: the object's finalizer has been
267       // run, all the object's references have been processed, and the
268       // object is unreachable.  There is, at long last, no way to
269       // resurrect it.
270       list->reference = DELETED_REFERENCE;
271       list->weight = DELETED;
272       --hash_count;
273       return;
274     }
275
276   enum weight w = head->weight;
277   if (w == FINALIZE)
278     {
279       // Update the list first, as _Jv_FinalizeString might end up
280       // looking at this data structure.
281       list->next = head->next;
282       _Jv_Free (head);
283
284       // If we have a Reference A to a Reference B, and B is
285       // finalized, then we have to take special care to make sure
286       // that B is properly deregistered.  This is super gross.  FIXME
287       // will it fail if B's finalizer resurrects B?
288       if (java::lang::ref::Reference::class$.isInstance (obj))
289         finalize_reference (obj);
290       else if (obj->getClass() == &java::lang::String::class$)
291         _Jv_FinalizeString (obj);
292       else
293         _Jv_FinalizeObject (obj);
294     }
295   else if (w != SOFT || _Jv_GCCanReclaimSoftReference (obj))
296     {
297       // If we just decided to reclaim a soft reference, we might as
298       // well do all the weak references at the same time.
299       if (w == SOFT)
300         w = WEAK;
301
302       while (head && head->weight <= w)
303         {
304           java::lang::ref::Reference *ref
305             = reinterpret_cast<java::lang::ref::Reference *> (head->reference);
306           if (! ref->cleared)
307             ref->enqueue ();
308
309           object_list *next = head->next;
310           _Jv_Free (head);
311           head = next;
312         }
313       list->next = head;
314     }
315
316   // Re-register this finalizer.  We always re-register because we
317   // can't know until the next collection cycle whether or not the
318   // object is truly unreachable.
319   _Jv_RegisterFinalizer (obj, finalize_referred_to_object);
320 }
321
322 // This is called when a Reference object is finalized.  If there is a
323 // Reference pointing to this Reference then that case is handled by
324 // finalize_referred_to_object.
325 static void
326 finalize_reference (jobject ref)
327 {
328   JvSynchronize sync (java::lang::ref::Reference::lock);
329   remove_from_hash (ref);
330   // The user might have a subclass of Reference with a finalizer.
331   _Jv_FinalizeObject (ref);
332 }
333
334 void
335 _Jv_RegisterStringFinalizer (jobject str)
336 {
337   // This function might be called before any other Reference method,
338   // so we must ensure the class is initialized.
339   _Jv_InitClass (&java::lang::ref::Reference::class$);
340   JvSynchronize sync (java::lang::ref::Reference::lock);
341   // If the object is in our hash table, then we might need to add a
342   // new FINALIZE entry.  Otherwise, we just register an ordinary
343   // finalizer.
344   object_list *entry = in_hash (str);
345   if (entry)
346     maybe_add_finalize (entry, str);
347   else
348     _Jv_RegisterFinalizer ((void *) str, _Jv_FinalizeString);
349 }
350
351 void
352 ::java::lang::ref::Reference::create (jobject ref)
353 {
354   // Nothing says you can't make a Reference with a NULL referent.
355   // But there's nothing to do in such a case.
356   referent = reinterpret_cast<gnu::gcj::RawData *> (ref);
357   copy = referent;
358   if (referent != NULL)
359     {
360       JvSynchronize sync (java::lang::ref::Reference::lock);
361       // `this' is a new Reference object.  We register a new
362       // finalizer for pointed-to object and we arrange a special
363       // finalizer for ourselves as well.
364       _Jv_RegisterFinalizer (this, finalize_reference);
365       _Jv_RegisterFinalizer (referent, finalize_referred_to_object);
366       gnu::gcj::RawData **p = &referent;
367      _Jv_GCRegisterDisappearingLink ((jobject *) p);
368       add_to_hash (this);
369     }
370 }
371
372 ::java::lang::Object *
373 ::java::lang::ref::Reference::get()
374 {
375   JvSynchronize sync (lock);
376   return referent;
377 }