Merge pull request #472 from MelanieT/spmanager_fix
[mono.git] / mono / metadata / sgen-fin-weak-hash.c
1 /*
2  * sgen-fin-weak-hash.c: Finalizers and weak links.
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *  Rodrigo Kumpera (kumpera@gmail.com)
7  *
8  * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10  * Copyright 2011 Xamarin, Inc.
11  * Copyright (C) 2012 Xamarin Inc
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Library General Public
15  * License 2.0 as published by the Free Software Foundation;
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library General Public
23  * License 2.0 along with this library; if not, write to the Free
24  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25  */
26
27 #include "config.h"
28 #ifdef HAVE_SGEN_GC
29
30 #include "metadata/sgen-gc.h"
31 #include "metadata/sgen-gray.h"
32 #include "utils/dtrace.h"
33
34 #define ptr_in_nursery sgen_ptr_in_nursery
35
36 typedef SgenGrayQueue GrayQueue;
37
38 int num_ready_finalizers = 0;
39 static int no_finalize = 0;
40
41 #define DISLINK_OBJECT(l)       (REVEAL_POINTER (*(void**)(l)))
42 #define DISLINK_TRACK(l)        ((~(gulong)(*(void**)(l))) & 1)
43
44 /*
45  * The finalizable hash has the object as the key, the 
46  * disappearing_link hash, has the link address as key.
47  *
48  * Copyright 2011 Xamarin Inc.
49  */
50
51 #define TAG_MASK ((mword)0x1)
52
53 static inline MonoObject*
54 tagged_object_get_object (MonoObject *object)
55 {
56         return (MonoObject*)(((mword)object) & ~TAG_MASK);
57 }
58
59 static inline int
60 tagged_object_get_tag (MonoObject *object)
61 {
62         return ((mword)object) & TAG_MASK;
63 }
64
65 static inline MonoObject*
66 tagged_object_apply (void *object, int tag_bits)
67 {
68        return (MonoObject*)((mword)object | (mword)tag_bits);
69 }
70
71 static int
72 tagged_object_hash (MonoObject *o)
73 {
74         return mono_object_hash (tagged_object_get_object (o));
75 }
76
77 static gboolean
78 tagged_object_equals (MonoObject *a, MonoObject *b)
79 {
80         return tagged_object_get_object (a) == tagged_object_get_object (b);
81 }
82
83 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);
84 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);
85
86 static SgenHashTable*
87 get_finalize_entry_hash_table (int generation)
88 {
89         switch (generation) {
90         case GENERATION_NURSERY: return &minor_finalizable_hash;
91         case GENERATION_OLD: return &major_finalizable_hash;
92         default: g_assert_not_reached ();
93         }
94 }
95
96 #define BRIDGE_OBJECT_MARKED 0x1
97
98 /* LOCKING: requires that the GC lock is held */
99 void
100 sgen_mark_bridge_object (MonoObject *obj)
101 {
102         SgenHashTable *hash_table = get_finalize_entry_hash_table (ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD);
103
104         sgen_hash_table_set_key (hash_table, obj, tagged_object_apply (obj, BRIDGE_OBJECT_MARKED));
105 }
106
107 /* LOCKING: requires that the GC lock is held */
108 void
109 sgen_collect_bridge_objects (int generation, ScanCopyContext ctx)
110 {
111         CopyOrMarkObjectFunc copy_func = ctx.copy_func;
112         GrayQueue *queue = ctx.queue;
113         SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
114         MonoObject *object;
115         gpointer dummy;
116         char *copy;
117
118         if (no_finalize)
119                 return;
120
121         SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
122                 int tag = tagged_object_get_tag (object);
123                 object = tagged_object_get_object (object);
124
125                 /* Bridge code told us to ignore this one */
126                 if (tag == BRIDGE_OBJECT_MARKED)
127                         continue;
128
129                 /* Object is a bridge object and major heap says it's dead  */
130                 if (major_collector.is_object_live ((char*)object))
131                         continue;
132
133                 /* Nursery says the object is dead. */
134                 if (!sgen_gc_is_object_ready_for_finalization (object))
135                         continue;
136
137                 if (!sgen_is_bridge_object (object))
138                         continue;
139
140                 copy = (char*)object;
141                 copy_func ((void**)&copy, queue);
142
143                 sgen_bridge_register_finalized_object ((MonoObject*)copy);
144                 
145                 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
146                         /* remove from the list */
147                         SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
148
149                         /* insert it into the major hash */
150                         sgen_hash_table_replace (&major_finalizable_hash, tagged_object_apply (copy, tag), NULL, NULL);
151
152                         SGEN_LOG (5, "Promoting finalization of object %p (%s) (was at %p) to major table", copy, sgen_safe_name (copy), object);
153
154                         continue;
155                 } else {
156                         /* update pointer */
157                         SGEN_LOG (5, "Updating object for finalization: %p (%s) (was at %p)", copy, sgen_safe_name (copy), object);
158                         SGEN_HASH_TABLE_FOREACH_SET_KEY (tagged_object_apply (copy, tag));
159                 }
160         } SGEN_HASH_TABLE_FOREACH_END;
161 }
162
163
164 /* LOCKING: requires that the GC lock is held */
165 void
166 sgen_finalize_in_range (int generation, ScanCopyContext ctx)
167 {
168         CopyOrMarkObjectFunc copy_func = ctx.copy_func;
169         GrayQueue *queue = ctx.queue;
170         SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
171         MonoObject *object;
172         gpointer dummy;
173
174         if (no_finalize)
175                 return;
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 (!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**)&copy, queue);
183                         if (is_fin_ready) {
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                                 SGEN_LOG (5, "Queueing object for finalization: %p (%s) (was at %p) (%d/%d)", copy, sgen_safe_name (copy), object, num_ready_finalizers, sgen_hash_table_num_entries (hash_table));
190                                 continue;
191                         } else {
192                                 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
193                                         /* remove from the list */
194                                         SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
195
196                                         /* insert it into the major hash */
197                                         sgen_hash_table_replace (&major_finalizable_hash, tagged_object_apply (copy, tag), NULL, NULL);
198
199                                         SGEN_LOG (5, "Promoting finalization of object %p (%s) (was at %p) to major table", copy, sgen_safe_name (copy), object);
200
201                                         continue;
202                                 } else {
203                                         /* update pointer */
204                                         SGEN_LOG (5, "Updating object for finalization: %p (%s) (was at %p)", copy, sgen_safe_name (copy), object);
205                                         SGEN_HASH_TABLE_FOREACH_SET_KEY (tagged_object_apply (copy, tag));
206                                 }
207                         }
208                 }
209         } SGEN_HASH_TABLE_FOREACH_END;
210 }
211
212 /* LOCKING: requires that the GC lock is held */
213 static void
214 register_for_finalization (MonoObject *obj, void *user_data, int generation)
215 {
216         SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
217
218         if (no_finalize)
219                 return;
220
221         g_assert (user_data == NULL || user_data == mono_gc_run_finalize);
222
223         if (user_data) {
224                 if (sgen_hash_table_replace (hash_table, obj, NULL, NULL))
225                         SGEN_LOG (5, "Added finalizer for object: %p (%s) (%d) to %s table", obj, obj->vtable->klass->name, hash_table->num_entries, sgen_generation_name (generation));
226         } else {
227                 if (sgen_hash_table_remove (hash_table, obj, NULL))
228                         SGEN_LOG (5, "Removed finalizer for object: %p (%s) (%d)", obj, obj->vtable->klass->name, hash_table->num_entries);
229         }
230 }
231
232 #define STAGE_ENTRY_FREE        0
233 #define STAGE_ENTRY_BUSY        1
234 #define STAGE_ENTRY_USED        2
235
236 typedef struct {
237         gint32 state;
238         MonoObject *obj;
239         void *user_data;
240 } StageEntry;
241
242 #define NUM_FIN_STAGE_ENTRIES   1024
243
244 static volatile gint32 next_fin_stage_entry = 0;
245 static StageEntry fin_stage_entries [NUM_FIN_STAGE_ENTRIES];
246
247 /* LOCKING: requires that the GC lock is held */
248 static void
249 process_stage_entries (int num_entries, volatile gint32 *next_entry, StageEntry *entries, void (*process_func) (MonoObject*, void*))
250 {
251         int i;
252         int num_registered = 0;
253         int num_busy = 0;
254
255         for (i = 0; i < num_entries; ++i) {
256                 gint32 state = entries [i].state;
257
258                 if (state == STAGE_ENTRY_BUSY)
259                         ++num_busy;
260
261                 if (state != STAGE_ENTRY_USED ||
262                                 InterlockedCompareExchange (&entries [i].state, STAGE_ENTRY_BUSY, STAGE_ENTRY_USED) != STAGE_ENTRY_USED) {
263                         continue;
264                 }
265
266                 process_func (entries [i].obj, entries [i].user_data);
267
268                 entries [i].obj = NULL;
269                 entries [i].user_data = NULL;
270
271                 mono_memory_write_barrier ();
272
273                 entries [i].state = STAGE_ENTRY_FREE;
274
275                 ++num_registered;
276         }
277
278         *next_entry = 0;
279
280         /* g_print ("stage busy %d reg %d\n", num_busy, num_registered); */
281 }
282
283 static gboolean
284 add_stage_entry (int num_entries, volatile gint32 *next_entry, StageEntry *entries, MonoObject *obj, void *user_data)
285 {
286         gint32 index;
287
288         do {
289                 do {
290                         index = *next_entry;
291                         if (index >= num_entries)
292                                 return FALSE;
293                 } while (InterlockedCompareExchange (next_entry, index + 1, index) != index);
294
295                 /*
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
298                  * correctness.
299                  */
300         } while (entries [index].state != STAGE_ENTRY_FREE ||
301                         InterlockedCompareExchange (&entries [index].state, STAGE_ENTRY_BUSY, STAGE_ENTRY_FREE) != STAGE_ENTRY_FREE);
302
303         entries [index].obj = obj;
304         entries [index].user_data = user_data;
305
306         mono_memory_write_barrier ();
307
308         entries [index].state = STAGE_ENTRY_USED;
309
310         return TRUE;
311 }
312
313 /* LOCKING: requires that the GC lock is held */
314 static void
315 process_fin_stage_entry (MonoObject *obj, void *user_data)
316 {
317         if (ptr_in_nursery (obj))
318                 register_for_finalization (obj, user_data, GENERATION_NURSERY);
319         else
320                 register_for_finalization (obj, user_data, GENERATION_OLD);
321 }
322
323 /* LOCKING: requires that the GC lock is held */
324 void
325 sgen_process_fin_stage_entries (void)
326 {
327         process_stage_entries (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry, fin_stage_entries, process_fin_stage_entry);
328 }
329
330 void
331 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
332 {
333         while (!add_stage_entry (NUM_FIN_STAGE_ENTRIES, &next_fin_stage_entry, fin_stage_entries, obj, user_data)) {
334                 LOCK_GC;
335                 sgen_process_fin_stage_entries ();
336                 UNLOCK_GC;
337         }
338 }
339
340 /* LOCKING: requires that the GC lock is held */
341 static int
342 finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size,
343         SgenHashTable *hash_table)
344 {
345         MonoObject *object;
346         gpointer dummy;
347         int count;
348
349         if (no_finalize || !out_size || !out_array)
350                 return 0;
351         count = 0;
352         SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
353                 object = tagged_object_get_object (object);
354
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                         SGEN_LOG (5, "Collecting object for finalization: %p (%s) (%d/%d)", object, sgen_safe_name (object), num_ready_finalizers, sgen_hash_table_num_entries (hash_table));
360                         if (count == out_size)
361                                 return count;
362                         continue;
363                 }
364         } SGEN_HASH_TABLE_FOREACH_END;
365         return count;
366 }
367
368 /**
369  * mono_gc_finalizers_for_domain:
370  * @domain: the unloading appdomain
371  * @out_array: output array
372  * @out_size: size of output array
373  *
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
378  * to finalize them.
379  * @out_array should be on the stack to allow the GC to know the objects are still alive.
380  */
381 int
382 mono_gc_finalizers_for_domain (MonoDomain *domain, MonoObject **out_array, int out_size)
383 {
384         int result;
385
386         LOCK_GC;
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);
392         }
393         UNLOCK_GC;
394
395         return result;
396 }
397
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);
400
401 static SgenHashTable*
402 get_dislink_hash_table (int generation)
403 {
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 ();
408         }
409 }
410
411 /* LOCKING: assumes the GC lock is held */
412 static void
413 add_or_remove_disappearing_link (MonoObject *obj, void **link, int generation)
414 {
415         SgenHashTable *hash_table = get_dislink_hash_table (generation);
416
417         if (!obj) {
418                 if (sgen_hash_table_remove (hash_table, link, NULL)) {
419                         SGEN_LOG (5, "Removed dislink %p (%d) from %s table",
420                                         link, hash_table->num_entries, sgen_generation_name (generation));
421                 }
422                 return;
423         }
424
425         sgen_hash_table_replace (hash_table, link, NULL, NULL);
426         SGEN_LOG (5, "Added dislink for object: %p (%s) at %p to %s table",
427                         obj, obj->vtable->klass->name, link, sgen_generation_name (generation));
428 }
429
430 /* LOCKING: requires that the GC lock is held */
431 void
432 sgen_null_link_in_range (int generation, gboolean before_finalization, ScanCopyContext ctx)
433 {
434         CopyOrMarkObjectFunc copy_func = ctx.copy_func;
435         GrayQueue *queue = ctx.queue;
436         void **link;
437         gpointer dummy;
438         SgenHashTable *hash = get_dislink_hash_table (generation);
439
440         SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
441                 char *object;
442                 gboolean track;
443
444                 /*
445                 We null a weak link before unregistering it, so it's possible that a thread is
446                 suspended right in between setting the content to null and staging the unregister.
447
448                 The rest of this code cannot handle null links as DISLINK_OBJECT (NULL) produces an invalid address.
449                 */
450                 if (!*link)
451                         continue;
452
453                 track = DISLINK_TRACK (link);
454                 /*
455                  * Tracked references are processed after
456                  * finalization handling whereas standard weak
457                  * references are processed before.  If an
458                  * object is still not marked after finalization
459                  * handling it means that it either doesn't have
460                  * a finalizer or the finalizer has already run,
461                  * so we must null a tracking reference.
462                  */
463                 if (track != before_finalization) {
464                         object = DISLINK_OBJECT (link);
465
466                         if (!major_collector.is_object_live (object)) {
467                                 if (sgen_gc_is_object_ready_for_finalization (object)) {
468                                         *link = NULL;
469                                         SGEN_LOG (5, "Dislink nullified at %p to GCed object %p", link, object);
470                                         SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
471                                         continue;
472                                 } else {
473                                         char *copy = object;
474                                         copy_func ((void**)&copy, queue);
475
476                                         /* Update pointer if it's moved.  If the object
477                                          * has been moved out of the nursery, we need to
478                                          * remove the link from the minor hash table to
479                                          * the major one.
480                                          *
481                                          * FIXME: what if an object is moved earlier?
482                                          */
483
484                                         if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
485                                                 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
486
487                                                 g_assert (copy);
488                                                 *link = HIDE_POINTER (copy, track);
489                                                 add_or_remove_disappearing_link ((MonoObject*)copy, link, GENERATION_OLD);
490
491                                                 SGEN_LOG (5, "Upgraded dislink at %p to major because object %p moved to %p", link, object, copy);
492
493                                                 continue;
494                                         } else {
495                                                 *link = HIDE_POINTER (copy, track);
496                                                 SGEN_LOG (5, "Updated dislink at %p to %p", link, DISLINK_OBJECT (link));
497                                         }
498                                 }
499                         }
500                 }
501         } SGEN_HASH_TABLE_FOREACH_END;
502 }
503
504 /* LOCKING: requires that the GC lock is held */
505 void
506 sgen_null_links_for_domain (MonoDomain *domain, int generation)
507 {
508         void **link;
509         gpointer dummy;
510         SgenHashTable *hash = get_dislink_hash_table (generation);
511         SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
512                 char *object = DISLINK_OBJECT (link);
513                 if (*link && object && !((MonoObject*)object)->vtable) {
514                         gboolean free = TRUE;
515
516                         if (*link) {
517                                 *link = NULL;
518                                 free = FALSE;
519                                 /*
520                                  * This can happen if finalizers are not ran, i.e. Environment.Exit ()
521                                  * is called from finalizer like in finalizer-abort.cs.
522                                  */
523                                 SGEN_LOG (5, "Disappearing link %p not freed", link);
524                         }
525
526                         SGEN_HASH_TABLE_FOREACH_REMOVE (free);
527
528                         continue;
529                 }
530         } SGEN_HASH_TABLE_FOREACH_END;
531 }
532
533 /* LOCKING: requires that the GC lock is held */
534 void
535 sgen_null_links_with_predicate (int generation, WeakLinkAlivePredicateFunc predicate, void *data)
536 {
537         void **link;
538         gpointer dummy;
539         SgenHashTable *hash = get_dislink_hash_table (generation);
540         SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
541                 char *object = DISLINK_OBJECT (link);
542                 mono_bool is_alive;
543
544                 if (!*link)
545                         continue;
546                 is_alive = predicate ((MonoObject*)object, data);
547
548                 if (!is_alive) {
549                         *link = NULL;
550                         SGEN_LOG (5, "Dislink nullified by predicate at %p to GCed object %p", link, object);
551                         SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
552                         continue;
553                 }
554         } SGEN_HASH_TABLE_FOREACH_END;
555 }
556
557 void
558 sgen_remove_finalizers_for_domain (MonoDomain *domain, int generation)
559 {
560         SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
561         MonoObject *object;
562         gpointer dummy;
563
564         SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
565                 object = tagged_object_get_object (object);
566
567                 if (mono_object_domain (object) == domain) {
568                         SGEN_LOG (5, "Unregistering finalizer for object: %p (%s)", object, sgen_safe_name (object));
569
570                         SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
571                         continue;
572                 }
573         } SGEN_HASH_TABLE_FOREACH_END;  
574 }
575
576 /* LOCKING: requires that the GC lock is held */
577 static void
578 process_dislink_stage_entry (MonoObject *obj, void *_link)
579 {
580         void **link = _link;
581
582         add_or_remove_disappearing_link (NULL, link, GENERATION_NURSERY);
583         add_or_remove_disappearing_link (NULL, link, GENERATION_OLD);
584         if (obj) {
585                 if (ptr_in_nursery (obj))
586                         add_or_remove_disappearing_link (obj, link, GENERATION_NURSERY);
587                 else
588                         add_or_remove_disappearing_link (obj, link, GENERATION_OLD);
589         }
590 }
591
592 #define NUM_DISLINK_STAGE_ENTRIES       1024
593
594 static volatile gint32 next_dislink_stage_entry = 0;
595 static StageEntry dislink_stage_entries [NUM_DISLINK_STAGE_ENTRIES];
596
597 /* LOCKING: requires that the GC lock is held */
598 void
599 sgen_process_dislink_stage_entries (void)
600 {
601         process_stage_entries (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, process_dislink_stage_entry);
602 }
603
604 void
605 sgen_register_disappearing_link (MonoObject *obj, void **link, gboolean track, gboolean in_gc)
606 {
607
608 #ifdef ENABLE_DTRACE
609         if (MONO_GC_WEAK_UPDATE_ENABLED ()) {
610                 MonoVTable *vt = obj ? (MonoVTable*)SGEN_LOAD_VTABLE (obj) : NULL;
611                 MONO_GC_WEAK_UPDATE ((mword)link,
612                                 *link ? (mword)DISLINK_OBJECT (link) : (mword)0,
613                                 (mword)obj,
614                                 obj ? (mword)sgen_safe_object_get_size (obj) : (mword)0,
615                                 obj ? vt->klass->name_space : NULL,
616                                 obj ? vt->klass->name : NULL,
617                                 track ? 1 : 0);
618         }
619 #endif
620
621         if (obj)
622                 *link = HIDE_POINTER (obj, track);
623         else
624                 *link = NULL;
625
626 #if 1
627         if (in_gc) {
628                 process_dislink_stage_entry (obj, link);
629         } else {
630                 while (!add_stage_entry (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, obj, link)) {
631                         LOCK_GC;
632                         sgen_process_dislink_stage_entries ();
633                         UNLOCK_GC;
634                 }
635         }
636 #else
637         if (!in_gc)
638                 LOCK_GC;
639         process_dislink_stage_entry (obj, link);
640         if (!in_gc)
641                 UNLOCK_GC;
642 #endif
643 }
644
645 #endif /* HAVE_SGEN_GC */