2 * sgen-fin-weak-hash.c:
5 * Paolo Molaro (lupus@ximian.com)
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10 * Copyright 2011 Xamarin, Inc.
12 * Permission is hereby granted, free of charge, to any person obtaining
13 * a copy of this software and associated documentation files (the
14 * "Software"), to deal in the Software without restriction, including
15 * without limitation the rights to use, copy, modify, merge, publish,
16 * distribute, sublicense, and/or sell copies of the Software, and to
17 * permit persons to whom the Software is furnished to do so, subject to
18 * the following conditions:
20 * The above copyright notice and this permission notice shall be
21 * included in all copies or substantial portions of the Software.
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 #include "metadata/sgen-gc.h"
36 #include "metadata/sgen-gray.h"
38 #define ptr_in_nursery sgen_ptr_in_nursery
40 typedef SgenGrayQueue GrayQueue;
42 int num_ready_finalizers = 0;
43 static int no_finalize = 0;
45 #define DISLINK_OBJECT(l) (REVEAL_POINTER (*(void**)(l)))
46 #define DISLINK_TRACK(l) ((~(gulong)(*(void**)(l))) & 1)
49 * The finalizable hash has the object as the key, the
50 * disappearing_link hash, has the link address as key.
52 * Copyright 2011 Xamarin Inc.
55 #define TAG_MASK ((mword)0x1)
57 static inline MonoObject*
58 tagged_object_get_object (MonoObject *object)
60 return (MonoObject*)(((mword)object) & ~TAG_MASK);
64 tagged_object_get_tag (MonoObject *object)
66 return ((mword)object) & TAG_MASK;
69 static inline MonoObject*
70 tagged_object_apply (void *object, int tag_bits)
72 return (MonoObject*)((mword)object | (mword)tag_bits);
76 tagged_object_hash (MonoObject *o)
78 return mono_object_hash (tagged_object_get_object (o));
82 tagged_object_equals (MonoObject *a, MonoObject *b)
84 return tagged_object_get_object (a) == tagged_object_get_object (b);
87 static SgenHashTable minor_finalizable_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_FIN_TABLE, INTERNAL_MEM_FINALIZE_ENTRY, 0, (GHashFunc)tagged_object_hash, (GEqualFunc)tagged_object_equals);
88 static SgenHashTable major_finalizable_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_FIN_TABLE, INTERNAL_MEM_FINALIZE_ENTRY, 0, (GHashFunc)tagged_object_hash, (GEqualFunc)tagged_object_equals);
91 get_finalize_entry_hash_table (int generation)
94 case GENERATION_NURSERY: return &minor_finalizable_hash;
95 case GENERATION_OLD: return &major_finalizable_hash;
96 default: g_assert_not_reached ();
100 #define BRIDGE_OBJECT_MARKED 0x1
102 /* LOCKING: requires that the GC lock is held */
104 sgen_mark_bridge_object (MonoObject *obj)
106 SgenHashTable *hash_table = get_finalize_entry_hash_table (ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD);
108 sgen_hash_table_set_key (hash_table, obj, tagged_object_apply (obj, BRIDGE_OBJECT_MARKED));
111 /* LOCKING: requires that the GC lock is held */
113 sgen_collect_bridge_objects (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
115 SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
123 SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
124 int tag = tagged_object_get_tag (object);
125 object = tagged_object_get_object (object);
127 /* Bridge code told us to ignore this one */
128 if (tag == BRIDGE_OBJECT_MARKED)
131 /* Object is a bridge object and major heap says it's dead */
132 if (!((char*)object >= start && (char*)object < end && !major_collector.is_object_live ((char*)object)))
135 /* Nursery says the object is dead. */
136 if (!sgen_gc_is_object_ready_for_finalization (object))
139 if (!sgen_is_bridge_object (object))
142 copy = (char*)object;
143 copy_func ((void**)©, queue);
145 sgen_bridge_register_finalized_object ((MonoObject*)copy);
147 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
148 /* remove from the list */
149 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
151 /* insert it into the major hash */
152 sgen_hash_table_replace (&major_finalizable_hash, tagged_object_apply (copy, tag), NULL, NULL);
154 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, sgen_safe_name (copy), object));
159 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", copy, sgen_safe_name (copy), object));
160 SGEN_HASH_TABLE_FOREACH_SET_KEY (tagged_object_apply (copy, tag));
162 } SGEN_HASH_TABLE_FOREACH_END;
166 /* LOCKING: requires that the GC lock is held */
168 sgen_finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
170 SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
176 SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
177 int tag = tagged_object_get_tag (object);
178 object = tagged_object_get_object (object);
179 if ((char*)object >= start && (char*)object < end && !major_collector.is_object_live ((char*)object)) {
180 gboolean is_fin_ready = sgen_gc_is_object_ready_for_finalization (object);
181 MonoObject *copy = object;
182 copy_func ((void**)©, queue);
184 /* remove and put in fin_ready_list */
185 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
186 num_ready_finalizers++;
187 sgen_queue_finalization_entry (copy);
188 /* Make it survive */
189 DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", copy, sgen_safe_name (copy), object, num_ready_finalizers, sgen_hash_table_num_entries (hash_table)));
192 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
193 /* remove from the list */
194 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
196 /* insert it into the major hash */
197 sgen_hash_table_replace (&major_finalizable_hash, tagged_object_apply (copy, tag), NULL, NULL);
199 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, sgen_safe_name (copy), object));
204 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", copy, sgen_safe_name (copy), object));
205 SGEN_HASH_TABLE_FOREACH_SET_KEY (tagged_object_apply (copy, tag));
209 } SGEN_HASH_TABLE_FOREACH_END;
212 /* LOCKING: requires that the GC lock is held */
214 register_for_finalization (MonoObject *obj, void *user_data, int generation)
216 SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
221 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
224 if (sgen_hash_table_replace (hash_table, obj, NULL, NULL))
225 DEBUG (5, fprintf (gc_debug_file, "Added finalizer for object: %p (%s) (%d) to %s table\n", obj, obj->vtable->klass->name, hash_table->num_entries, sgen_generation_name (generation)));
227 if (sgen_hash_table_remove (hash_table, obj, NULL))
228 DEBUG (5, fprintf (gc_debug_file, "Removed finalizer for object: %p (%s) (%d)\n", obj, obj->vtable->klass->name, hash_table->num_entries));
232 #define STAGE_ENTRY_FREE 0
233 #define STAGE_ENTRY_BUSY 1
234 #define STAGE_ENTRY_USED 2
242 #define NUM_FIN_STAGE_ENTRIES 1024
244 static volatile gint32 next_fin_stage_entry = 0;
245 static StageEntry fin_stage_entries [NUM_FIN_STAGE_ENTRIES];
247 /* LOCKING: requires that the GC lock is held */
249 process_stage_entries (int num_entries, volatile gint32 *next_entry, StageEntry *entries, void (*process_func) (MonoObject*, void*))
252 int num_registered = 0;
255 for (i = 0; i < num_entries; ++i) {
256 gint32 state = entries [i].state;
258 if (state == STAGE_ENTRY_BUSY)
261 if (state != STAGE_ENTRY_USED ||
262 InterlockedCompareExchange (&entries [i].state, STAGE_ENTRY_BUSY, STAGE_ENTRY_USED) != STAGE_ENTRY_USED) {
266 process_func (entries [i].obj, entries [i].user_data);
268 entries [i].obj = NULL;
269 entries [i].user_data = NULL;
271 mono_memory_write_barrier ();
273 entries [i].state = STAGE_ENTRY_FREE;
280 /* g_print ("stage busy %d reg %d\n", num_busy, num_registered); */
284 add_stage_entry (int num_entries, volatile gint32 *next_entry, StageEntry *entries, MonoObject *obj, void *user_data)
291 if (index >= num_entries)
293 } while (InterlockedCompareExchange (next_entry, index + 1, index) != index);
296 * We don't need a write barrier here. *next_entry is just a
297 * help for finding an index, its value is irrelevant for
300 } while (entries [index].state != STAGE_ENTRY_FREE ||
301 InterlockedCompareExchange (&entries [index].state, STAGE_ENTRY_BUSY, STAGE_ENTRY_FREE) != STAGE_ENTRY_FREE);
303 entries [index].obj = obj;
304 entries [index].user_data = user_data;
306 mono_memory_write_barrier ();
308 entries [index].state = STAGE_ENTRY_USED;
313 /* LOCKING: requires that the GC lock is held */
315 process_fin_stage_entry (MonoObject *obj, void *user_data)
317 if (ptr_in_nursery (obj))
318 register_for_finalization (obj, user_data, GENERATION_NURSERY);
320 register_for_finalization (obj, user_data, GENERATION_OLD);
323 /* LOCKING: requires that the GC lock is held */
325 sgen_process_fin_stage_entries (void)
327 process_stage_entries (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry, fin_stage_entries, process_fin_stage_entry);
331 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
333 while (!add_stage_entry (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry, fin_stage_entries, obj, user_data)) {
335 sgen_process_fin_stage_entries ();
340 /* LOCKING: requires that the GC lock is held */
342 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
343 SgenHashTable *hash_table)
349 if (no_finalize || !out_size || !out_array)
352 SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
353 object = tagged_object_get_object (object);
355 if (mono_object_domain (object) == domain) {
356 /* remove and put in out_array */
357 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
358 out_array [count ++] = object;
359 DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", object, sgen_safe_name (object), num_ready_finalizers, sgen_hash_table_num_entries (hash_table)));
360 if (count == out_size)
364 } SGEN_HASH_TABLE_FOREACH_END;
369 * mono_gc_finalizers_for_domain:
370 * @domain: the unloading appdomain
371 * @out_array: output array
372 * @out_size: size of output array
374 * Store inside @out_array up to @out_size objects that belong to the unloading
375 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
376 * until it returns 0.
377 * The items are removed from the finalizer data structure, so the caller is supposed
379 * @out_array should be on the stack to allow the GC to know the objects are still alive.
382 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
387 sgen_process_fin_stage_entries ();
388 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
389 if (result < out_size) {
390 result += finalizers_for_domain (domain, out_array + result, out_size - result,
391 &major_finalizable_hash);
398 static SgenHashTable minor_disappearing_link_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_DISLINK_TABLE, INTERNAL_MEM_DISLINK, 0, mono_aligned_addr_hash, NULL);
399 static SgenHashTable major_disappearing_link_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_DISLINK_TABLE, INTERNAL_MEM_DISLINK, 0, mono_aligned_addr_hash, NULL);
401 static SgenHashTable*
402 get_dislink_hash_table (int generation)
404 switch (generation) {
405 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
406 case GENERATION_OLD: return &major_disappearing_link_hash;
407 default: g_assert_not_reached ();
411 /* LOCKING: assumes the GC lock is held */
413 add_or_remove_disappearing_link (MonoObject *obj, void **link, int generation)
415 SgenHashTable *hash_table = get_dislink_hash_table (generation);
418 if (sgen_hash_table_remove (hash_table, link, NULL)) {
419 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n",
420 link, hash_table->num_entries, sgen_generation_name (generation)));
425 sgen_hash_table_replace (hash_table, link, NULL, NULL);
426 DEBUG (5, fprintf (gc_debug_file, "Added dislink for object: %p (%s) at %p to %s table\n",
427 obj, obj->vtable->klass->name, link, sgen_generation_name (generation)));
430 /* LOCKING: requires that the GC lock is held */
432 sgen_null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue)
436 SgenHashTable *hash = get_dislink_hash_table (generation);
438 SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
440 gboolean track = DISLINK_TRACK (link);
443 * Tracked references are processed after
444 * finalization handling whereas standard weak
445 * references are processed before. If an
446 * object is still not marked after finalization
447 * handling it means that it either doesn't have
448 * a finalizer or the finalizer has already run,
449 * so we must null a tracking reference.
451 if (track != before_finalization) {
452 object = DISLINK_OBJECT (link);
454 if (object >= start && object < end && !major_collector.is_object_live (object)) {
455 if (sgen_gc_is_object_ready_for_finalization (object)) {
457 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", link, object));
458 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
462 copy_func ((void**)©, queue);
464 /* Update pointer if it's moved. If the object
465 * has been moved out of the nursery, we need to
466 * remove the link from the minor hash table to
469 * FIXME: what if an object is moved earlier?
472 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
473 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
476 *link = HIDE_POINTER (copy, track);
477 add_or_remove_disappearing_link ((MonoObject*)copy, link, GENERATION_OLD);
479 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
483 *link = HIDE_POINTER (copy, track);
484 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", link, DISLINK_OBJECT (link)));
489 } SGEN_HASH_TABLE_FOREACH_END;
492 /* LOCKING: requires that the GC lock is held */
494 sgen_null_links_for_domain (MonoDomain *domain, int generation)
498 SgenHashTable *hash = get_dislink_hash_table (generation);
499 SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
500 char *object = DISLINK_OBJECT (link);
501 if (object && !((MonoObject*)object)->vtable) {
502 gboolean free = TRUE;
508 * This can happen if finalizers are not ran, i.e. Environment.Exit ()
509 * is called from finalizer like in finalizer-abort.cs.
511 DEBUG (5, fprintf (gc_debug_file, "Disappearing link %p not freed", link));
514 SGEN_HASH_TABLE_FOREACH_REMOVE (free);
518 } SGEN_HASH_TABLE_FOREACH_END;
521 /* LOCKING: requires that the GC lock is held */
523 sgen_null_links_with_predicate (int generation, WeakLinkAlivePredicateFunc predicate, void *data)
527 SgenHashTable *hash = get_dislink_hash_table (generation);
528 fprintf (stderr, "**** nulling links with predicate\n");
529 SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
530 char *object = DISLINK_OBJECT (link);
531 mono_bool is_alive = predicate ((MonoObject*)object, data);
534 fprintf (stderr, "ALIVE %p %s\n", object, sgen_safe_name (object));
536 fprintf (stderr, "DEAD %p %s\n", object, sgen_safe_name (object));
540 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified by predicate at %p to GCed object %p\n", link, object));
541 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
544 } SGEN_HASH_TABLE_FOREACH_END;
548 sgen_remove_finalizers_for_domain (MonoDomain *domain, int generation)
550 SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
554 SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
555 object = tagged_object_get_object (object);
557 if (mono_object_domain (object) == domain) {
558 DEBUG (5, fprintf (gc_debug_file, "Unregistering finalizer for object: %p (%s)\n", object, sgen_safe_name (object)));
560 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
563 } SGEN_HASH_TABLE_FOREACH_END;
566 /* LOCKING: requires that the GC lock is held */
568 process_dislink_stage_entry (MonoObject *obj, void *_link)
572 add_or_remove_disappearing_link (NULL, link, GENERATION_NURSERY);
573 add_or_remove_disappearing_link (NULL, link, GENERATION_OLD);
575 if (ptr_in_nursery (obj))
576 add_or_remove_disappearing_link (obj, link, GENERATION_NURSERY);
578 add_or_remove_disappearing_link (obj, link, GENERATION_OLD);
582 #define NUM_DISLINK_STAGE_ENTRIES 1024
584 static volatile gint32 next_dislink_stage_entry = 0;
585 static StageEntry dislink_stage_entries [NUM_DISLINK_STAGE_ENTRIES];
587 /* LOCKING: requires that the GC lock is held */
589 sgen_process_dislink_stage_entries (void)
591 process_stage_entries (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, process_dislink_stage_entry);
595 sgen_register_disappearing_link (MonoObject *obj, void **link, gboolean track, gboolean in_gc)
598 *link = HIDE_POINTER (obj, track);
604 process_dislink_stage_entry (obj, link);
606 while (!add_stage_entry (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, obj, link)) {
608 sgen_process_dislink_stage_entries ();
615 process_dislink_stage_entry (obj, link);
621 #endif /* HAVE_SGEN_GC */