1 #define DISLINK_OBJECT(l) (REVEAL_POINTER (*(void**)(l)))
2 #define DISLINK_TRACK(l) ((~(gulong)(*(void**)(l))) & 1)
5 * The finalizable hash has the object as the key, the
6 * disappearing_link hash, has the link address as key.
8 * Copyright 2011 Xamarin Inc.
11 #define TAG_MASK ((mword)0x1)
13 static inline MonoObject*
14 tagged_object_get_object (MonoObject *object)
16 return (MonoObject*)(((mword)object) & ~TAG_MASK);
20 tagged_object_get_tag (MonoObject *object)
22 return ((mword)object) & TAG_MASK;
25 static inline MonoObject*
26 tagged_object_apply (void *object, int tag_bits)
28 return (MonoObject*)((mword)object | (mword)tag_bits);
32 tagged_object_hash (MonoObject *o)
34 return mono_object_hash (tagged_object_get_object (o));
38 tagged_object_equals (MonoObject *a, MonoObject *b)
40 return tagged_object_get_object (a) == tagged_object_get_object (b);
43 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);
44 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);
47 get_finalize_entry_hash_table (int generation)
50 case GENERATION_NURSERY: return &minor_finalizable_hash;
51 case GENERATION_OLD: return &major_finalizable_hash;
52 default: g_assert_not_reached ();
56 #define BRIDGE_OBJECT_MARKED 0x1
58 /* LOCKING: requires that the GC lock is held */
60 sgen_mark_bridge_object (MonoObject *obj)
62 SgenHashTable *hash_table = get_finalize_entry_hash_table (ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD);
64 sgen_hash_table_set_key (hash_table, obj, tagged_object_apply (obj, BRIDGE_OBJECT_MARKED));
67 /* LOCKING: requires that the GC lock is held */
69 collect_bridge_objects (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
71 SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
79 SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
80 int tag = tagged_object_get_tag (object);
81 object = tagged_object_get_object (object);
83 /* Bridge code told us to ignore this one */
84 if (tag == BRIDGE_OBJECT_MARKED)
87 /* Object is a bridge object and major heap says it's dead */
88 if (!((char*)object >= start && (char*)object < end && !major_collector.is_object_live ((char*)object)))
91 /* Nursery says the object is dead. */
92 if (!sgen_gc_is_object_ready_for_finalization (object))
95 if (!sgen_is_bridge_object (object))
99 copy_func ((void**)©, queue);
101 sgen_bridge_register_finalized_object ((MonoObject*)copy);
103 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
104 /* remove from the list */
105 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
107 /* insert it into the major hash */
108 sgen_hash_table_replace (&major_finalizable_hash, tagged_object_apply (copy, tag), NULL, NULL);
110 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), object));
115 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", copy, safe_name (copy), object));
116 SGEN_HASH_TABLE_FOREACH_SET_KEY (tagged_object_apply (copy, tag));
118 } SGEN_HASH_TABLE_FOREACH_END;
122 /* LOCKING: requires that the GC lock is held */
124 finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
126 SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
132 SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
133 int tag = tagged_object_get_tag (object);
134 object = tagged_object_get_object (object);
135 if ((char*)object >= start && (char*)object < end && !major_collector.is_object_live ((char*)object)) {
136 gboolean is_fin_ready = sgen_gc_is_object_ready_for_finalization (object);
137 MonoObject *copy = object;
138 copy_func ((void**)©, queue);
140 /* remove and put in fin_ready_list */
141 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
142 num_ready_finalizers++;
143 queue_finalization_entry (copy);
144 /* Make it survive */
145 DEBUG (5, fprintf (gc_debug_file, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)\n", copy, safe_name (copy), object, num_ready_finalizers, sgen_hash_table_num_entries (hash_table)));
148 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
149 /* remove from the list */
150 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
152 /* insert it into the major hash */
153 sgen_hash_table_replace (&major_finalizable_hash, tagged_object_apply (copy, tag), NULL, NULL);
155 DEBUG (5, fprintf (gc_debug_file, "Promoting finalization of object %p (%s) (was at %p) to major table\n", copy, safe_name (copy), object));
160 DEBUG (5, fprintf (gc_debug_file, "Updating object for finalization: %p (%s) (was at %p)\n", copy, safe_name (copy), object));
161 SGEN_HASH_TABLE_FOREACH_SET_KEY (tagged_object_apply (copy, tag));
165 } SGEN_HASH_TABLE_FOREACH_END;
168 /* LOCKING: requires that the GC lock is held */
170 register_for_finalization (MonoObject *obj, void *user_data, int generation)
172 SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
177 g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
180 if (sgen_hash_table_replace (hash_table, obj, NULL, NULL))
181 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, generation_name (generation)));
183 if (sgen_hash_table_remove (hash_table, obj, NULL))
184 DEBUG (5, fprintf (gc_debug_file, "Removed finalizer for object: %p (%s) (%d)\n", obj, obj->vtable->klass->name, hash_table->num_entries));
188 #define STAGE_ENTRY_FREE 0
189 #define STAGE_ENTRY_BUSY 1
190 #define STAGE_ENTRY_USED 2
198 #define NUM_FIN_STAGE_ENTRIES 1024
200 static volatile gint32 next_fin_stage_entry = 0;
201 static StageEntry fin_stage_entries [NUM_FIN_STAGE_ENTRIES];
203 /* LOCKING: requires that the GC lock is held */
205 process_stage_entries (int num_entries, volatile gint32 *next_entry, StageEntry *entries, void (*process_func) (MonoObject*, void*))
208 int num_registered = 0;
211 for (i = 0; i < num_entries; ++i) {
212 gint32 state = entries [i].state;
214 if (state == STAGE_ENTRY_BUSY)
217 if (state != STAGE_ENTRY_USED ||
218 InterlockedCompareExchange (&entries [i].state, STAGE_ENTRY_BUSY, STAGE_ENTRY_USED) != STAGE_ENTRY_USED) {
222 process_func (entries [i].obj, entries [i].user_data);
224 entries [i].obj = NULL;
225 entries [i].user_data = NULL;
227 mono_memory_write_barrier ();
229 entries [i].state = STAGE_ENTRY_FREE;
236 /* g_print ("stage busy %d reg %d\n", num_busy, num_registered); */
240 add_stage_entry (int num_entries, volatile gint32 *next_entry, StageEntry *entries, MonoObject *obj, void *user_data)
247 if (index >= num_entries)
249 } while (InterlockedCompareExchange (next_entry, index + 1, index) != index);
252 * We don't need a write barrier here. *next_entry is just a
253 * help for finding an index, its value is irrelevant for
256 } while (entries [index].state != STAGE_ENTRY_FREE ||
257 InterlockedCompareExchange (&entries [index].state, STAGE_ENTRY_BUSY, STAGE_ENTRY_FREE) != STAGE_ENTRY_FREE);
259 entries [index].obj = obj;
260 entries [index].user_data = user_data;
262 mono_memory_write_barrier ();
264 entries [index].state = STAGE_ENTRY_USED;
269 /* LOCKING: requires that the GC lock is held */
271 process_fin_stage_entry (MonoObject *obj, void *user_data)
273 if (ptr_in_nursery (obj))
274 register_for_finalization (obj, user_data, GENERATION_NURSERY);
276 register_for_finalization (obj, user_data, GENERATION_OLD);
279 /* LOCKING: requires that the GC lock is held */
281 process_fin_stage_entries (void)
283 process_stage_entries (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry, fin_stage_entries, process_fin_stage_entry);
287 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
289 while (!add_stage_entry (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry, fin_stage_entries, obj, user_data)) {
291 process_fin_stage_entries ();
296 /* LOCKING: requires that the GC lock is held */
298 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
299 SgenHashTable *hash_table)
305 if (no_finalize || !out_size || !out_array)
308 SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
309 object = tagged_object_get_object (object);
311 if (mono_object_domain (object) == domain) {
312 /* remove and put in out_array */
313 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
314 out_array [count ++] = object;
315 DEBUG (5, fprintf (gc_debug_file, "Collecting object for finalization: %p (%s) (%d/%d)\n", object, safe_name (object), num_ready_finalizers, sgen_hash_table_num_entries (hash_table)));
316 if (count == out_size)
320 } SGEN_HASH_TABLE_FOREACH_END;
325 * mono_gc_finalizers_for_domain:
326 * @domain: the unloading appdomain
327 * @out_array: output array
328 * @out_size: size of output array
330 * Store inside @out_array up to @out_size objects that belong to the unloading
331 * appdomain @domain. Returns the number of stored items. Can be called repeteadly
332 * until it returns 0.
333 * The items are removed from the finalizer data structure, so the caller is supposed
335 * @out_array should be on the stack to allow the GC to know the objects are still alive.
338 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
343 process_fin_stage_entries ();
344 result = finalizers_for_domain (domain, out_array, out_size, &minor_finalizable_hash);
345 if (result < out_size) {
346 result += finalizers_for_domain (domain, out_array + result, out_size - result,
347 &major_finalizable_hash);
354 static SgenHashTable minor_disappearing_link_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_DISLINK_TABLE, INTERNAL_MEM_DISLINK, 0, mono_aligned_addr_hash, NULL);
355 static SgenHashTable major_disappearing_link_hash = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_DISLINK_TABLE, INTERNAL_MEM_DISLINK, 0, mono_aligned_addr_hash, NULL);
357 static SgenHashTable*
358 get_dislink_hash_table (int generation)
360 switch (generation) {
361 case GENERATION_NURSERY: return &minor_disappearing_link_hash;
362 case GENERATION_OLD: return &major_disappearing_link_hash;
363 default: g_assert_not_reached ();
367 /* LOCKING: assumes the GC lock is held */
369 add_or_remove_disappearing_link (MonoObject *obj, void **link, int generation)
371 SgenHashTable *hash_table = get_dislink_hash_table (generation);
374 if (sgen_hash_table_remove (hash_table, link, NULL)) {
375 DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n",
376 link, hash_table->num_entries, generation_name (generation)));
381 sgen_hash_table_replace (hash_table, link, NULL, NULL);
382 DEBUG (5, fprintf (gc_debug_file, "Added dislink for object: %p (%s) at %p to %s table\n",
383 obj, obj->vtable->klass->name, link, generation_name (generation)));
386 /* LOCKING: requires that the GC lock is held */
388 null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue)
392 SgenHashTable *hash = get_dislink_hash_table (generation);
394 SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
396 gboolean track = DISLINK_TRACK (link);
399 * Tracked references are processed after
400 * finalization handling whereas standard weak
401 * references are processed before. If an
402 * object is still not marked after finalization
403 * handling it means that it either doesn't have
404 * a finalizer or the finalizer has already run,
405 * so we must null a tracking reference.
407 if (track != before_finalization) {
408 object = DISLINK_OBJECT (link);
410 if (object >= start && object < end && !major_collector.is_object_live (object)) {
411 if (sgen_gc_is_object_ready_for_finalization (object)) {
413 DEBUG (5, fprintf (gc_debug_file, "Dislink nullified at %p to GCed object %p\n", link, object));
414 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
418 copy_func ((void**)©, queue);
420 /* Update pointer if it's moved. If the object
421 * has been moved out of the nursery, we need to
422 * remove the link from the minor hash table to
425 * FIXME: what if an object is moved earlier?
428 if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
429 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
432 *link = HIDE_POINTER (copy, track);
433 add_or_remove_disappearing_link ((MonoObject*)copy, link, GENERATION_OLD);
435 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
439 *link = HIDE_POINTER (copy, track);
440 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", link, DISLINK_OBJECT (link)));
445 } SGEN_HASH_TABLE_FOREACH_END;
448 /* LOCKING: requires that the GC lock is held */
450 null_links_for_domain (MonoDomain *domain, int generation)
454 SgenHashTable *hash = get_dislink_hash_table (generation);
455 SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
456 char *object = DISLINK_OBJECT (link);
457 if (object && !((MonoObject*)object)->vtable) {
458 gboolean free = TRUE;
464 * This can happen if finalizers are not ran, i.e. Environment.Exit ()
465 * is called from finalizer like in finalizer-abort.cs.
467 DEBUG (5, fprintf (gc_debug_file, "Disappearing link %p not freed", link));
470 SGEN_HASH_TABLE_FOREACH_REMOVE (free);
474 } SGEN_HASH_TABLE_FOREACH_END;
478 remove_finalizers_for_domain (MonoDomain *domain, int generation)
480 SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
484 SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
485 object = tagged_object_get_object (object);
487 if (mono_object_domain (object) == domain) {
488 DEBUG (5, fprintf (gc_debug_file, "Unregistering finalizer for object: %p (%s)\n", object, safe_name (object)));
490 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
493 } SGEN_HASH_TABLE_FOREACH_END;
496 /* LOCKING: requires that the GC lock is held */
498 process_dislink_stage_entry (MonoObject *obj, void *_link)
502 add_or_remove_disappearing_link (NULL, link, GENERATION_NURSERY);
503 add_or_remove_disappearing_link (NULL, link, GENERATION_OLD);
505 if (ptr_in_nursery (obj))
506 add_or_remove_disappearing_link (obj, link, GENERATION_NURSERY);
508 add_or_remove_disappearing_link (obj, link, GENERATION_OLD);
512 #define NUM_DISLINK_STAGE_ENTRIES 1024
514 static volatile gint32 next_dislink_stage_entry = 0;
515 static StageEntry dislink_stage_entries [NUM_DISLINK_STAGE_ENTRIES];
517 /* LOCKING: requires that the GC lock is held */
519 process_dislink_stage_entries (void)
521 process_stage_entries (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, process_dislink_stage_entry);
525 mono_gc_register_disappearing_link (MonoObject *obj, void **link, gboolean track, gboolean in_gc)
528 *link = HIDE_POINTER (obj, track);
534 process_dislink_stage_entry (obj, link);
536 while (!add_stage_entry (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, obj, link)) {
538 process_dislink_stage_entries ();
545 process_dislink_stage_entry (obj, link);