do not check order sequence if option /order was not used
[mono.git] / mono / metadata / sgen-fin-weak-hash.c
1 /*
2  * sgen-fin-weak-hash.c:
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  *
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:
19  * 
20  * The above copyright notice and this permission notice shall be
21  * included in all copies or substantial portions of the Software.
22  * 
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.
30  */
31
32 #include "config.h"
33 #ifdef HAVE_SGEN_GC
34
35 #include "metadata/sgen-gc.h"
36 #include "metadata/sgen-gray.h"
37
38 #define ptr_in_nursery sgen_ptr_in_nursery
39
40 typedef SgenGrayQueue GrayQueue;
41
42 int num_ready_finalizers = 0;
43 static int no_finalize = 0;
44
45 #define DISLINK_OBJECT(l)       (REVEAL_POINTER (*(void**)(l)))
46 #define DISLINK_TRACK(l)        ((~(gulong)(*(void**)(l))) & 1)
47
48 /*
49  * The finalizable hash has the object as the key, the 
50  * disappearing_link hash, has the link address as key.
51  *
52  * Copyright 2011 Xamarin Inc.
53  */
54
55 #define TAG_MASK ((mword)0x1)
56
57 static inline MonoObject*
58 tagged_object_get_object (MonoObject *object)
59 {
60         return (MonoObject*)(((mword)object) & ~TAG_MASK);
61 }
62
63 static inline int
64 tagged_object_get_tag (MonoObject *object)
65 {
66         return ((mword)object) & TAG_MASK;
67 }
68
69 static inline MonoObject*
70 tagged_object_apply (void *object, int tag_bits)
71 {
72        return (MonoObject*)((mword)object | (mword)tag_bits);
73 }
74
75 static int
76 tagged_object_hash (MonoObject *o)
77 {
78         return mono_object_hash (tagged_object_get_object (o));
79 }
80
81 static gboolean
82 tagged_object_equals (MonoObject *a, MonoObject *b)
83 {
84         return tagged_object_get_object (a) == tagged_object_get_object (b);
85 }
86
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);
89
90 static SgenHashTable*
91 get_finalize_entry_hash_table (int generation)
92 {
93         switch (generation) {
94         case GENERATION_NURSERY: return &minor_finalizable_hash;
95         case GENERATION_OLD: return &major_finalizable_hash;
96         default: g_assert_not_reached ();
97         }
98 }
99
100 #define BRIDGE_OBJECT_MARKED 0x1
101
102 /* LOCKING: requires that the GC lock is held */
103 void
104 sgen_mark_bridge_object (MonoObject *obj)
105 {
106         SgenHashTable *hash_table = get_finalize_entry_hash_table (ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD);
107
108         sgen_hash_table_set_key (hash_table, obj, tagged_object_apply (obj, BRIDGE_OBJECT_MARKED));
109 }
110
111 /* LOCKING: requires that the GC lock is held */
112 void
113 sgen_collect_bridge_objects (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
114 {
115         SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
116         MonoObject *object;
117         gpointer dummy;
118         char *copy;
119
120         if (no_finalize)
121                 return;
122
123         SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
124                 int tag = tagged_object_get_tag (object);
125                 object = tagged_object_get_object (object);
126
127                 /* Bridge code told us to ignore this one */
128                 if (tag == BRIDGE_OBJECT_MARKED)
129                         continue;
130
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)))
133                         continue;
134
135                 /* Nursery says the object is dead. */
136                 if (!sgen_gc_is_object_ready_for_finalization (object))
137                         continue;
138
139                 if (!sgen_is_bridge_object (object))
140                         continue;
141
142                 copy = (char*)object;
143                 copy_func ((void**)&copy, queue);
144
145                 sgen_bridge_register_finalized_object ((MonoObject*)copy);
146                 
147                 if (hash_table == &minor_finalizable_hash && !ptr_in_nursery (copy)) {
148                         /* remove from the list */
149                         SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
150
151                         /* insert it into the major hash */
152                         sgen_hash_table_replace (&major_finalizable_hash, tagged_object_apply (copy, tag), NULL, NULL);
153
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));
155
156                         continue;
157                 } else {
158                         /* update pointer */
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));
161                 }
162         } SGEN_HASH_TABLE_FOREACH_END;
163 }
164
165
166 /* LOCKING: requires that the GC lock is held */
167 void
168 sgen_finalize_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, GrayQueue *queue)
169 {
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 ((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**)&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                                 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)));
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                                         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));
200
201                                         continue;
202                                 } else {
203                                         /* update pointer */
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));
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                         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)));
226         } else {
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));
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                         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)
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                         DEBUG (5, fprintf (gc_debug_file, "Removed dislink %p (%d) from %s table\n",
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         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)));
428 }
429
430 /* LOCKING: requires that the GC lock is held */
431 void
432 sgen_null_link_in_range (CopyOrMarkObjectFunc copy_func, char *start, char *end, int generation, gboolean before_finalization, GrayQueue *queue)
433 {
434         void **link;
435         gpointer dummy;
436         SgenHashTable *hash = get_dislink_hash_table (generation);
437
438         SGEN_HASH_TABLE_FOREACH (hash, link, dummy) {
439                 char *object;
440                 gboolean track = DISLINK_TRACK (link);
441
442                 /*
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.
450                  */
451                 if (track != before_finalization) {
452                         object = DISLINK_OBJECT (link);
453
454                         if (object >= start && object < end && !major_collector.is_object_live (object)) {
455                                 if (sgen_gc_is_object_ready_for_finalization (object)) {
456                                         *link = NULL;
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);
459                                         continue;
460                                 } else {
461                                         char *copy = object;
462                                         copy_func ((void**)&copy, queue);
463
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
467                                          * the major one.
468                                          *
469                                          * FIXME: what if an object is moved earlier?
470                                          */
471
472                                         if (hash == &minor_disappearing_link_hash && !ptr_in_nursery (copy)) {
473                                                 SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
474
475                                                 g_assert (copy);
476                                                 *link = HIDE_POINTER (copy, track);
477                                                 add_or_remove_disappearing_link ((MonoObject*)copy, link, GENERATION_OLD);
478
479                                                 DEBUG (5, fprintf (gc_debug_file, "Upgraded dislink at %p to major because object %p moved to %p\n", link, object, copy));
480
481                                                 continue;
482                                         } else {
483                                                 *link = HIDE_POINTER (copy, track);
484                                                 DEBUG (5, fprintf (gc_debug_file, "Updated dislink at %p to %p\n", link, DISLINK_OBJECT (link)));
485                                         }
486                                 }
487                         }
488                 }
489         } SGEN_HASH_TABLE_FOREACH_END;
490 }
491
492 /* LOCKING: requires that the GC lock is held */
493 void
494 sgen_null_links_for_domain (MonoDomain *domain, int generation)
495 {
496         void **link;
497         gpointer dummy;
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;
503
504                         if (*link) {
505                                 *link = NULL;
506                                 free = FALSE;
507                                 /*
508                                  * This can happen if finalizers are not ran, i.e. Environment.Exit ()
509                                  * is called from finalizer like in finalizer-abort.cs.
510                                  */
511                                 DEBUG (5, fprintf (gc_debug_file, "Disappearing link %p not freed", link));
512                         }
513
514                         SGEN_HASH_TABLE_FOREACH_REMOVE (free);
515
516                         continue;
517                 }
518         } SGEN_HASH_TABLE_FOREACH_END;
519 }
520
521 /* LOCKING: requires that the GC lock is held */
522 void
523 sgen_null_links_with_predicate (int generation, WeakLinkAlivePredicateFunc predicate, void *data)
524 {
525         void **link;
526         gpointer dummy;
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);
532
533                 if (is_alive)
534                         fprintf (stderr, "ALIVE %p %s\n", object, sgen_safe_name (object));
535                 else
536                         fprintf (stderr, "DEAD %p %s\n", object, sgen_safe_name (object));
537
538                 if (!is_alive) {
539                         *link = NULL;
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);
542                         continue;
543                 }
544         } SGEN_HASH_TABLE_FOREACH_END;
545 }
546
547 void
548 sgen_remove_finalizers_for_domain (MonoDomain *domain, int generation)
549 {
550         SgenHashTable *hash_table = get_finalize_entry_hash_table (generation);
551         MonoObject *object;
552         gpointer dummy;
553
554         SGEN_HASH_TABLE_FOREACH (hash_table, object, dummy) {
555                 object = tagged_object_get_object (object);
556
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)));
559
560                         SGEN_HASH_TABLE_FOREACH_REMOVE (TRUE);
561                         continue;
562                 }
563         } SGEN_HASH_TABLE_FOREACH_END;  
564 }
565
566 /* LOCKING: requires that the GC lock is held */
567 static void
568 process_dislink_stage_entry (MonoObject *obj, void *_link)
569 {
570         void **link = _link;
571
572         add_or_remove_disappearing_link (NULL, link, GENERATION_NURSERY);
573         add_or_remove_disappearing_link (NULL, link, GENERATION_OLD);
574         if (obj) {
575                 if (ptr_in_nursery (obj))
576                         add_or_remove_disappearing_link (obj, link, GENERATION_NURSERY);
577                 else
578                         add_or_remove_disappearing_link (obj, link, GENERATION_OLD);
579         }
580 }
581
582 #define NUM_DISLINK_STAGE_ENTRIES       1024
583
584 static volatile gint32 next_dislink_stage_entry = 0;
585 static StageEntry dislink_stage_entries [NUM_DISLINK_STAGE_ENTRIES];
586
587 /* LOCKING: requires that the GC lock is held */
588 void
589 sgen_process_dislink_stage_entries (void)
590 {
591         process_stage_entries (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, process_dislink_stage_entry);
592 }
593
594 void
595 sgen_register_disappearing_link (MonoObject *obj, void **link, gboolean track, gboolean in_gc)
596 {
597         if (obj)
598                 *link = HIDE_POINTER (obj, track);
599         else
600                 *link = NULL;
601
602 #if 1
603         if (in_gc) {
604                 process_dislink_stage_entry (obj, link);
605         } else {
606                 while (!add_stage_entry (NUM_DISLINK_STAGE_ENTRIES, &next_dislink_stage_entry, dislink_stage_entries, obj, link)) {
607                         LOCK_GC;
608                         sgen_process_dislink_stage_entries ();
609                         UNLOCK_GC;
610                 }
611         }
612 #else
613         if (!in_gc)
614                 LOCK_GC;
615         process_dislink_stage_entry (obj, link);
616         if (!in_gc)
617                 UNLOCK_GC;
618 #endif
619 }
620
621 #endif /* HAVE_SGEN_GC */