1 | // natReference.cc - Native code for References
|
---|
2 |
|
---|
3 | /* Copyright (C) 2001, 2002 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 | |
---|
28 |
|
---|
29 |
|
---|
30 | enum weight
|
---|
31 | {
|
---|
32 | SOFT = 0,
|
---|
33 | WEAK = 1,
|
---|
34 | FINALIZE = 2,
|
---|
35 | PHANTOM = 3,
|
---|
36 |
|
---|
37 | // This is used to mark the head of a list.
|
---|
38 | HEAD = 4,
|
---|
39 |
|
---|
40 | // This is used to mark a deleted item.
|
---|
41 | DELETED = 5
|
---|
42 | };
|
---|
43 |
|
---|
44 | // Objects of this type are used in the hash table to keep track of
|
---|
45 | // the mapping between a finalizable object and the various References
|
---|
46 | // which refer to it.
|
---|
47 | struct object_list
|
---|
48 | {
|
---|
49 | // The reference object. This is NULL for FINALIZE weight.
|
---|
50 | jobject reference;
|
---|
51 |
|
---|
52 | // The weight of this object.
|
---|
53 | enum weight weight;
|
---|
54 |
|
---|
55 | // Next in list.
|
---|
56 | object_list *next;
|
---|
57 | };
|
---|
58 |
|
---|
59 | // Hash table used to hold mapping from object to References. The
|
---|
60 | // object_list item in the hash holds the object itself in the
|
---|
61 | // reference field; chained to it are all the references sorted in
|
---|
62 | // order of weight (lowest first).
|
---|
63 | static object_list *hash = NULL;
|
---|
64 |
|
---|
65 | // Number of slots used in HASH.
|
---|
66 | static int hash_count = 0;
|
---|
67 |
|
---|
68 | // Number of slots total in HASH. Must be power of 2.
|
---|
69 | static int hash_size = 0;
|
---|
70 |
|
---|
71 | static object_list *
|
---|
72 | find_slot (jobject key)
|
---|
73 | {
|
---|
74 | jint hcode = _Jv_HashCode (key);
|
---|
75 | /* step must be non-zero, and relatively prime with hash_size. */
|
---|
76 | jint step = (hcode ^ (hcode >> 16)) | 1;
|
---|
77 | int start_index = hcode & (hash_size - 1);
|
---|
78 | int index = start_index;
|
---|
79 | int deleted_index = -1;
|
---|
80 | for (;;)
|
---|
81 | {
|
---|
82 | object_list *ptr = &hash[index];
|
---|
83 | if (ptr->reference == key)
|
---|
84 | return ptr;
|
---|
85 | else if (ptr->reference == NULL)
|
---|
86 | {
|
---|
87 | if (deleted_index == -1)
|
---|
88 | return ptr;
|
---|
89 | else
|
---|
90 | return &hash[deleted_index];
|
---|
91 | }
|
---|
92 | else if (ptr->weight == DELETED)
|
---|
93 | deleted_index = index;
|
---|
94 | index = (index + step) & (hash_size - 1);
|
---|
95 | JvAssert (index != start_index);
|
---|
96 | }
|
---|
97 | }
|
---|
98 |
|
---|
99 | static void
|
---|
100 | rehash ()
|
---|
101 | {
|
---|
102 | if (hash == NULL)
|
---|
103 | {
|
---|
104 | hash_size = 1024;
|
---|
105 | hash = (object_list *) _Jv_Malloc (hash_size * sizeof (object_list));
|
---|
106 | memset (hash, 0, hash_size * sizeof (object_list));
|
---|
107 | }
|
---|
108 | else
|
---|
109 | {
|
---|
110 | object_list *old = hash;
|
---|
111 | int i = hash_size;
|
---|
112 |
|
---|
113 | hash_size *= 2;
|
---|
114 | hash = (object_list *) _Jv_Malloc (hash_size * sizeof (object_list));
|
---|
115 | memset (hash, 0, hash_size * sizeof (object_list));
|
---|
116 |
|
---|
117 | while (--i >= 0)
|
---|
118 | {
|
---|
119 | if (old[i].reference == NULL || old[i].weight == DELETED)
|
---|
120 | continue;
|
---|
121 | object_list *newslot = find_slot (old[i].reference);
|
---|
122 | *newslot = old[i];
|
---|
123 | }
|
---|
124 |
|
---|
125 | _Jv_Free (old);
|
---|
126 | }
|
---|
127 | }
|
---|
128 |
|
---|
129 | // Remove a Reference.
|
---|
130 | static void
|
---|
131 | remove_from_hash (jobject obj)
|
---|
132 | {
|
---|
133 | java::lang::ref::Reference *ref
|
---|
134 | = reinterpret_cast<java::lang::ref::Reference *> (obj);
|
---|
135 | object_list *head = find_slot (ref->copy);
|
---|
136 | object_list **link = &head->next;
|
---|
137 | head = head->next;
|
---|
138 |
|
---|
139 | while (head && head->reference != ref)
|
---|
140 | {
|
---|
141 | link = &head->next;
|
---|
142 | head = head->next;
|
---|
143 | }
|
---|
144 |
|
---|
145 | // Remove the slot.
|
---|
146 | if (head)
|
---|
147 | {
|
---|
148 | *link = head->next;
|
---|
149 | _Jv_Free (head);
|
---|
150 | }
|
---|
151 | }
|
---|
152 |
|
---|
153 | // FIXME what happens if an object's finalizer creates a Reference to
|
---|
154 | // the object, and the object has never before been added to the hash?
|
---|
155 | // Madness!
|
---|
156 |
|
---|
157 | // Add an item to the hash table. If the item is new, we also add a
|
---|
158 | // finalizer item. We keep items in the hash table until they are
|
---|
159 | // completely collected; this lets us know when an item is new, even
|
---|
160 | // if it has been resurrected after its finalizer has been run.
|
---|
161 | static void
|
---|
162 | add_to_hash (java::lang::ref::Reference *the_reference)
|
---|
163 | {
|
---|
164 | JvSynchronize sync (java::lang::ref::Reference::lock);
|
---|
165 |
|
---|
166 | if (3 * hash_count >= 2 * hash_size)
|
---|
167 | rehash ();
|
---|
168 |
|
---|
169 | // Use `copy' here because the `referent' field has been cleared.
|
---|
170 | jobject referent = the_reference->copy;
|
---|
171 | object_list *item = find_slot (referent);
|
---|
172 | if (item->reference == NULL)
|
---|
173 | {
|
---|
174 | // New item, so make an entry for the finalizer.
|
---|
175 | item->reference = referent;
|
---|
176 | item->weight = HEAD;
|
---|
177 |
|
---|
178 | item->next = (object_list *) _Jv_Malloc (sizeof (object_list));
|
---|
179 | item->next->reference = NULL;
|
---|
180 | item->next->weight = FINALIZE;
|
---|
181 | item->next->next = NULL;
|
---|
182 | ++hash_count;
|
---|
183 | }
|
---|
184 |
|
---|
185 | object_list *n = (object_list *) _Jv_Malloc (sizeof (object_list));
|
---|
186 | n->reference = the_reference;
|
---|
187 |
|
---|
188 | enum weight w = PHANTOM;
|
---|
189 | if (java::lang::ref::SoftReference::class$.isInstance (the_reference))
|
---|
190 | w = SOFT;
|
---|
191 | else if (java::lang::ref::WeakReference::class$.isInstance (the_reference))
|
---|
192 | w = WEAK;
|
---|
193 | n->weight = w;
|
---|
194 |
|
---|
195 | object_list **link = &item->next;
|
---|
196 | object_list *iter = *link;
|
---|
197 | while (iter && iter->weight < n->weight)
|
---|
198 | {
|
---|
199 | link = &iter->next;
|
---|
200 | iter = *link;
|
---|
201 | }
|
---|
202 | n->next = *link;
|
---|
203 | *link = n;
|
---|
204 | }
|
---|
205 |
|
---|
206 | // This is called when an object is ready to be finalized. This
|
---|
207 | // actually implements the appropriate Reference semantics.
|
---|
208 | static void
|
---|
209 | finalize_referred_to_object (jobject obj)
|
---|
210 | {
|
---|
211 | JvSynchronize sync (java::lang::ref::Reference::lock);
|
---|
212 |
|
---|
213 | object_list *list = find_slot (obj);
|
---|
214 | object_list *head = list->next;
|
---|
215 | if (head == NULL)
|
---|
216 | {
|
---|
217 | // We have a truly dead object: the object's finalizer has been
|
---|
218 | // run, all the object's references have been processed, and the
|
---|
219 | // object is unreachable. There is, at long last, no way to
|
---|
220 | // resurrect it.
|
---|
221 | list->weight = DELETED;
|
---|
222 | --hash_count;
|
---|
223 | return;
|
---|
224 | }
|
---|
225 |
|
---|
226 | enum weight w = head->weight;
|
---|
227 | if (w == FINALIZE)
|
---|
228 | {
|
---|
229 | // If we have a Reference A to a Reference B, and B is
|
---|
230 | // finalized, then we have to take special care to make sure
|
---|
231 | // that B is properly deregistered. This is super gross. FIXME
|
---|
232 | // will it fail if B's finalizer resurrects B?
|
---|
233 | if (java::lang::ref::Reference::class$.isInstance (obj))
|
---|
234 | finalize_reference (obj);
|
---|
235 | else
|
---|
236 | _Jv_FinalizeObject (obj);
|
---|
237 | list->next = head->next;
|
---|
238 | _Jv_Free (head);
|
---|
239 | }
|
---|
240 | else if (w != SOFT || _Jv_GCCanReclaimSoftReference (obj))
|
---|
241 | {
|
---|
242 | // If we just decided to reclaim a soft reference, we might as
|
---|
243 | // well do all the weak references at the same time.
|
---|
244 | if (w == SOFT)
|
---|
245 | w = WEAK;
|
---|
246 |
|
---|
247 | while (head && head->weight <= w)
|
---|
248 | {
|
---|
249 | java::lang::ref::Reference *ref
|
---|
250 | = reinterpret_cast<java::lang::ref::Reference *> (head->reference);
|
---|
251 | // If the copy is already NULL then the user must have
|
---|
252 | // called Reference.clear().
|
---|
253 | if (ref->copy != NULL)
|
---|
254 | ref->enqueue ();
|
---|
255 |
|
---|
256 | object_list *next = head->next;
|
---|
257 | _Jv_Free (head);
|
---|
258 | head = next;
|
---|
259 | }
|
---|
260 | list->next = head;
|
---|
261 | }
|
---|
262 |
|
---|
263 | // Re-register this finalizer. We always re-register because we
|
---|
264 | // can't know until the next collection cycle whether or not the
|
---|
265 | // object is truly unreachable.
|
---|
266 | _Jv_RegisterFinalizer (obj, finalize_referred_to_object);
|
---|
267 | }
|
---|
268 |
|
---|
269 | // This is called when a Reference object is finalized. If there is a
|
---|
270 | // Reference pointing to this Reference then that case is handled by
|
---|
271 | // finalize_referred_to_object.
|
---|
272 | static void
|
---|
273 | finalize_reference (jobject ref)
|
---|
274 | {
|
---|
275 | JvSynchronize sync (java::lang::ref::Reference::lock);
|
---|
276 | remove_from_hash (ref);
|
---|
277 | // The user might have a subclass of Reference with a finalizer.
|
---|
278 | _Jv_FinalizeObject (ref);
|
---|
279 | }
|
---|
280 |
|
---|
281 | void
|
---|
282 | ::java::lang::ref::Reference::create (jobject ref)
|
---|
283 | {
|
---|
284 | // Nothing says you can't make a Reference with a NULL referent.
|
---|
285 | // But there's nothing to do in such a case.
|
---|
286 | referent = reinterpret_cast<gnu::gcj::RawData *> (ref);
|
---|
287 | copy = referent;
|
---|
288 | if (referent != NULL)
|
---|
289 | {
|
---|
290 | JvSynchronize sync (java::lang::ref::Reference::lock);
|
---|
291 | // `this' is a new Reference object. We register a new
|
---|
292 | // finalizer for pointed-to object and we arrange a special
|
---|
293 | // finalizer for ourselves as well.
|
---|
294 | _Jv_RegisterFinalizer (this, finalize_reference);
|
---|
295 | _Jv_RegisterFinalizer (referent, finalize_referred_to_object);
|
---|
296 | jobject *objp = reinterpret_cast<jobject *> (&referent);
|
---|
297 | _Jv_GCRegisterDisappearingLink (objp);
|
---|
298 | add_to_hash (this);
|
---|
299 | }
|
---|
300 | }
|
---|