[docs] Enable documentation for sgen.
[mono.git] / mono / sgen / sgen-gchandles.c
1 /**
2  * \file
3  * SGen GC handles.
4  *
5  * Copyright (C) 2015 Xamarin Inc
6  *
7  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8  */
9
10 #include "config.h"
11 #ifdef HAVE_SGEN_GC
12
13 #include "mono/sgen/sgen-gc.h"
14 #include "mono/sgen/sgen-client.h"
15 #include "mono/sgen/sgen-array-list.h"
16 #include "mono/utils/mono-membar.h"
17
18 #ifdef HEAVY_STATISTICS
19 static volatile guint32 stat_gc_handles_allocated = 0;
20 static volatile guint32 stat_gc_handles_max_allocated = 0;
21 #endif
22
23 /*
24  * A table of GC handle data, implementing a simple lock-free bitmap allocator.
25  *
26  * Each entry in a bucket is a pointer with two tag bits: if
27  * 'GC_HANDLE_OCCUPIED' returns true for a slot, then the slot is occupied; if
28  * so, then 'GC_HANDLE_VALID' gives whether the entry refers to a valid (1) or
29  * NULL (0) object reference. If the reference is valid, then the pointer is an
30  * object pointer. If the reference is NULL, and 'GC_HANDLE_TYPE_IS_WEAK' is
31  * true for 'type', then the pointer is a metadata pointer--this allows us to
32  * retrieve the domain ID of an expired weak reference in Mono.
33  */
34
35 typedef struct {
36         SgenArrayList entries_array;
37         guint8 type;
38 } HandleData;
39
40 static void
41 protocol_gchandle_update (int handle_type, gpointer link, gpointer old_value, gpointer new_value)
42 {
43         gboolean old = MONO_GC_HANDLE_IS_OBJECT_POINTER (old_value);
44         gboolean new_ = MONO_GC_HANDLE_IS_OBJECT_POINTER (new_value);
45         gboolean track = handle_type == HANDLE_WEAK_TRACK;
46
47         if (!MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type))
48                 return;
49
50         if (!old && new_)
51                 binary_protocol_dislink_add (link, MONO_GC_REVEAL_POINTER (new_value, TRUE), track);
52         else if (old && !new_)
53                 binary_protocol_dislink_remove (link, track);
54         else if (old && new_ && old_value != new_value)
55                 binary_protocol_dislink_update (link, MONO_GC_REVEAL_POINTER (new_value, TRUE), track);
56 }
57
58 /* Returns the new value in the slot, or NULL if the CAS failed. */
59 static inline gpointer
60 try_set_slot (volatile gpointer *slot, GCObject *obj, gpointer old, GCHandleType type)
61 {
62         gpointer new_;
63         if (obj)
64                 new_ = MONO_GC_HANDLE_OBJECT_POINTER (obj, GC_HANDLE_TYPE_IS_WEAK (type));
65         else
66                 new_ = MONO_GC_HANDLE_METADATA_POINTER (sgen_client_default_metadata (), GC_HANDLE_TYPE_IS_WEAK (type));
67         SGEN_ASSERT (0, new_, "Why is the occupied bit not set?");
68         if (InterlockedCompareExchangePointer (slot, new_, old) == old) {
69                 protocol_gchandle_update (type, (gpointer)slot, old, new_);
70                 return new_;
71         }
72         return NULL;
73 }
74
75 static inline gboolean
76 is_slot_set (volatile gpointer *slot)
77 {
78         gpointer entry = *slot;
79         if (MONO_GC_HANDLE_OCCUPIED (entry))
80                 return TRUE;
81         return FALSE;
82 }
83
84 /* Try to claim a slot by setting its occupied bit. */
85 static inline gboolean
86 try_occupy_slot (volatile gpointer *slot, gpointer obj, int data)
87 {
88         if (is_slot_set (slot))
89                 return FALSE;
90         return try_set_slot (slot, (GCObject *)obj, NULL, (GCHandleType)data) != NULL;
91 }
92
93 static void
94 bucket_alloc_callback (gpointer *bucket, guint32 new_bucket_size, gboolean alloc)
95 {
96         if (alloc)
97                 sgen_register_root ((char *)bucket, new_bucket_size, SGEN_DESCRIPTOR_NULL, ROOT_TYPE_PINNED, MONO_ROOT_SOURCE_GC_HANDLE, "pinned gc handles");
98         else
99                 sgen_deregister_root ((char *)bucket);
100 }
101
102 static HandleData gc_handles [] = {
103         { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_WEAK) },
104         { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_WEAK_TRACK) },
105         { SGEN_ARRAY_LIST_INIT (NULL, is_slot_set, try_occupy_slot, -1), (HANDLE_NORMAL) },
106         { SGEN_ARRAY_LIST_INIT (bucket_alloc_callback, is_slot_set, try_occupy_slot, -1), (HANDLE_PINNED) }
107 };
108
109 static HandleData *
110 gc_handles_for_type (GCHandleType type)
111 {
112         return type < HANDLE_TYPE_MAX ? &gc_handles [type] : NULL;
113 }
114
115 /* This assumes that the world is stopped. */
116 void
117 sgen_mark_normal_gc_handles (void *addr, SgenUserMarkFunc mark_func, void *gc_data)
118 {
119         HandleData *handles = gc_handles_for_type (HANDLE_NORMAL);
120         SgenArrayList *array = &handles->entries_array;
121         volatile gpointer *slot;
122         gpointer hidden, revealed;
123
124         SGEN_ARRAY_LIST_FOREACH_SLOT (array, slot) {
125                 hidden = *slot;
126                 revealed = MONO_GC_REVEAL_POINTER (hidden, FALSE);
127                 if (!MONO_GC_HANDLE_IS_OBJECT_POINTER (hidden))
128                         continue;
129                 mark_func ((MonoObject **)&revealed, gc_data);
130                 g_assert (revealed);
131                 *slot = MONO_GC_HANDLE_OBJECT_POINTER (revealed, FALSE);
132         } SGEN_ARRAY_LIST_END_FOREACH_SLOT;
133 }
134
135
136 static guint32
137 alloc_handle (HandleData *handles, GCObject *obj, gboolean track)
138 {
139         guint32 res, index;
140         SgenArrayList *array = &handles->entries_array;
141
142         /*
143          * If a GC happens shortly after a new bucket is allocated, the entire
144          * bucket could be scanned even though it's mostly empty. To avoid this,
145          * we track the maximum index seen so far, so that we can skip the empty
146          * slots.
147          *
148          * Note that we update `next_slot` before we even try occupying the
149          * slot.  If we did it the other way around and a GC happened in
150          * between, the GC wouldn't know that the slot was occupied.  This is
151          * not a huge deal since `obj` is on the stack and thus pinned anyway,
152          * but hopefully some day it won't be anymore.
153          */
154         index = sgen_array_list_add (array, obj, handles->type, TRUE);
155 #ifdef HEAVY_STATISTICS
156         InterlockedIncrement ((volatile gint32 *)&stat_gc_handles_allocated);
157         if (stat_gc_handles_allocated > stat_gc_handles_max_allocated)
158                 stat_gc_handles_max_allocated = stat_gc_handles_allocated;
159 #endif
160         /* Ensure that a GC handle cannot be given to another thread without the slot having been set. */
161         mono_memory_write_barrier ();
162         res = MONO_GC_HANDLE (index, handles->type);
163         sgen_client_gchandle_created (handles->type, obj, res);
164         return res;
165 }
166
167 static gboolean
168 object_older_than (GCObject *object, int generation)
169 {
170         return generation == GENERATION_NURSERY && !sgen_ptr_in_nursery (object);
171 }
172
173 /*
174  * Maps a function over all GC handles.
175  * This assumes that the world is stopped!
176  */
177 void
178 sgen_gchandle_iterate (GCHandleType handle_type, int max_generation, SgenGCHandleIterateCallback callback, gpointer user)
179 {
180         HandleData *handle_data = gc_handles_for_type (handle_type);
181         SgenArrayList *array = &handle_data->entries_array;
182         gpointer hidden, result, occupied;
183         volatile gpointer *slot;
184
185         /* If a new bucket has been allocated, but the capacity has not yet been
186          * increased, nothing can yet have been allocated in the bucket because the
187          * world is stopped, so we shouldn't miss any handles during iteration.
188          */
189         SGEN_ARRAY_LIST_FOREACH_SLOT (array, slot) {
190                 hidden = *slot;
191                 occupied = (gpointer) MONO_GC_HANDLE_OCCUPIED (hidden);
192                 g_assert (hidden ? !!occupied : !occupied);
193                 if (!occupied)
194                         continue;
195                 result = callback (hidden, handle_type, max_generation, user);
196                 if (result)
197                         SGEN_ASSERT (0, MONO_GC_HANDLE_OCCUPIED (result), "Why did the callback return an unoccupied entry?");
198                 else
199                         HEAVY_STAT (InterlockedDecrement ((volatile gint32 *)&stat_gc_handles_allocated));
200                 protocol_gchandle_update (handle_type, (gpointer)slot, hidden, result);
201                 *slot = result;
202         } SGEN_ARRAY_LIST_END_FOREACH_SLOT;
203 }
204
205 /**
206  * mono_gchandle_new:
207  * @obj: managed object to get a handle for
208  * @pinned: whether the object should be pinned
209  *
210  * This returns a handle that wraps the object, this is used to keep a
211  * reference to a managed object from the unmanaged world and preventing the
212  * object from being disposed.
213  * 
214  * If @pinned is false the address of the object can not be obtained, if it is
215  * true the address of the object can be obtained.  This will also pin the
216  * object so it will not be possible by a moving garbage collector to move the
217  * object. 
218  * 
219  * Returns: a handle that can be used to access the object from
220  * unmanaged code.
221  */
222 guint32
223 mono_gchandle_new (GCObject *obj, gboolean pinned)
224 {
225         return alloc_handle (gc_handles_for_type (pinned ? HANDLE_PINNED : HANDLE_NORMAL), obj, FALSE);
226 }
227
228 /**
229  * mono_gchandle_new_weakref:
230  * @obj: managed object to get a handle for
231  * @track_resurrection: Determines how long to track the object, if this is set to TRUE, the object is tracked after finalization, if FALSE, the object is only tracked up until the point of finalization.
232  *
233  * This returns a weak handle that wraps the object, this is used to
234  * keep a reference to a managed object from the unmanaged world.
235  * Unlike the mono_gchandle_new the object can be reclaimed by the
236  * garbage collector.  In this case the value of the GCHandle will be
237  * set to zero.
238  * 
239  * If @track_resurrection is TRUE the object will be tracked through
240  * finalization and if the object is resurrected during the execution
241  * of the finalizer, then the returned weakref will continue to hold
242  * a reference to the object.   If @track_resurrection is FALSE, then
243  * the weak reference's target will become NULL as soon as the object
244  * is passed on to the finalizer.
245  * 
246  * Returns: a handle that can be used to access the object from
247  * unmanaged code.
248  */
249 guint32
250 mono_gchandle_new_weakref (GCObject *obj, gboolean track_resurrection)
251 {
252         return alloc_handle (gc_handles_for_type (track_resurrection ? HANDLE_WEAK_TRACK : HANDLE_WEAK), obj, track_resurrection);
253 }
254
255 static GCObject *
256 link_get (volatile gpointer *link_addr, gboolean is_weak)
257 {
258         void *volatile *link_addr_volatile;
259         void *ptr;
260         GCObject *obj;
261 retry:
262         link_addr_volatile = link_addr;
263         ptr = (void*)*link_addr_volatile;
264         /*
265          * At this point we have a hidden pointer.  If the GC runs
266          * here, it will not recognize the hidden pointer as a
267          * reference, and if the object behind it is not referenced
268          * elsewhere, it will be freed.  Once the world is restarted
269          * we reveal the pointer, giving us a pointer to a freed
270          * object.  To make sure we don't return it, we load the
271          * hidden pointer again.  If it's still the same, we can be
272          * sure the object reference is valid.
273          */
274         if (ptr && MONO_GC_HANDLE_IS_OBJECT_POINTER (ptr))
275                 obj = (GCObject *)MONO_GC_REVEAL_POINTER (ptr, is_weak);
276         else
277                 return NULL;
278
279         /* Note [dummy use]:
280          *
281          * If a GC happens here, obj needs to be on the stack or in a
282          * register, so we need to prevent this from being reordered
283          * wrt the check.
284          */
285         sgen_dummy_use (obj);
286         mono_memory_barrier ();
287
288         if (is_weak)
289                 sgen_client_ensure_weak_gchandles_accessible ();
290
291         if ((void*)*link_addr_volatile != ptr)
292                 goto retry;
293
294         return obj;
295 }
296
297 /**
298  * mono_gchandle_get_target:
299  * @gchandle: a GCHandle's handle.
300  *
301  * The handle was previously created by calling `mono_gchandle_new` or
302  * `mono_gchandle_new_weakref`. 
303  *
304  * Returns a pointer to the `MonoObject*` represented by the handle or
305  * NULL for a collected object if using a weakref handle.
306  */
307 GCObject*
308 mono_gchandle_get_target (guint32 gchandle)
309 {
310         guint index = MONO_GC_HANDLE_SLOT (gchandle);
311         GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
312         HandleData *handles = gc_handles_for_type (type);
313         /* Invalid handles are possible; accessing one should produce NULL. (#34276) */
314         if (!handles)
315                 return NULL;
316         return link_get (sgen_array_list_get_slot (&handles->entries_array, index), MONO_GC_HANDLE_TYPE_IS_WEAK (type));
317 }
318
319 void
320 sgen_gchandle_set_target (guint32 gchandle, GCObject *obj)
321 {
322         guint32 index = MONO_GC_HANDLE_SLOT (gchandle);
323         GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
324         HandleData *handles = gc_handles_for_type (type);
325         volatile gpointer *slot;
326         gpointer entry;
327
328         if (!handles)
329                 return;
330
331         slot = sgen_array_list_get_slot (&handles->entries_array, index);
332
333         do {
334                 entry = *slot;
335                 SGEN_ASSERT (0, MONO_GC_HANDLE_OCCUPIED (entry), "Why are we setting the target on an unoccupied slot?");
336         } while (!try_set_slot (slot, obj, entry, (GCHandleType)handles->type));
337 }
338
339 static gpointer
340 mono_gchandle_slot_metadata (volatile gpointer *slot, gboolean is_weak)
341 {
342         gpointer entry;
343         gpointer metadata;
344 retry:
345         entry = *slot;
346         if (!MONO_GC_HANDLE_OCCUPIED (entry))
347                 return NULL;
348         if (MONO_GC_HANDLE_IS_OBJECT_POINTER (entry)) {
349                 GCObject *obj = (GCObject *)MONO_GC_REVEAL_POINTER (entry, is_weak);
350                 /* See note [dummy use]. */
351                 sgen_dummy_use (obj);
352                 /*
353                  * FIXME: The compiler could technically not carry a reference to obj around
354                  * at this point and recompute it later, in which case we would still use
355                  * it.
356                  */
357                 if (*slot != entry)
358                         goto retry;
359                 return sgen_client_metadata_for_object (obj);
360         }
361         metadata = MONO_GC_REVEAL_POINTER (entry, is_weak);
362         /* See note [dummy use]. */
363         sgen_dummy_use (metadata);
364         if (*slot != entry)
365                 goto retry;
366         return metadata;
367 }
368
369 gpointer
370 sgen_gchandle_get_metadata (guint32 gchandle)
371 {
372         guint32 index = MONO_GC_HANDLE_SLOT (gchandle);
373         GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
374         HandleData *handles = gc_handles_for_type (type);
375         volatile gpointer *slot;
376
377         if (!handles)
378                 return NULL;
379         if (index >= handles->entries_array.capacity)
380                 return NULL;
381
382         slot = sgen_array_list_get_slot (&handles->entries_array, index);
383
384         return mono_gchandle_slot_metadata (slot, MONO_GC_HANDLE_TYPE_IS_WEAK (type));
385 }
386
387 /**
388  * mono_gchandle_free:
389  * @gchandle: a GCHandle's handle.
390  *
391  * Frees the @gchandle handle.  If there are no outstanding
392  * references, the garbage collector can reclaim the memory of the
393  * object wrapped. 
394  */
395 void
396 mono_gchandle_free (guint32 gchandle)
397 {
398         guint32 index = MONO_GC_HANDLE_SLOT (gchandle);
399         GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
400         HandleData *handles = gc_handles_for_type (type);
401         volatile gpointer *slot;
402         gpointer entry;
403         if (!handles)
404                 return;
405
406         slot = sgen_array_list_get_slot (&handles->entries_array, index);
407         entry = *slot;
408
409         if (index < handles->entries_array.capacity && MONO_GC_HANDLE_OCCUPIED (entry)) {
410                 *slot = NULL;
411                 protocol_gchandle_update (handles->type, (gpointer)slot, entry, NULL);
412                 HEAVY_STAT (InterlockedDecrement ((volatile gint32 *)&stat_gc_handles_allocated));
413         } else {
414                 /* print a warning? */
415         }
416         sgen_client_gchandle_destroyed (handles->type, gchandle);
417 }
418
419 /*
420  * Returns whether to remove the link from its hash.
421  */
422 static gpointer
423 null_link_if_necessary (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user)
424 {
425         const gboolean is_weak = GC_HANDLE_TYPE_IS_WEAK (handle_type);
426         ScanCopyContext *ctx = (ScanCopyContext *)user;
427         GCObject *obj;
428         GCObject *copy;
429
430         if (!MONO_GC_HANDLE_VALID (hidden))
431                 return hidden;
432
433         obj = (GCObject *)MONO_GC_REVEAL_POINTER (hidden, MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type));
434         SGEN_ASSERT (0, obj, "Why is the hidden pointer NULL?");
435
436         if (object_older_than (obj, max_generation))
437                 return hidden;
438
439         if (major_collector.is_object_live (obj))
440                 return hidden;
441
442         /* Clear link if object is ready for finalization. This check may be redundant wrt is_object_live(). */
443         if (sgen_gc_is_object_ready_for_finalization (obj))
444                 return MONO_GC_HANDLE_METADATA_POINTER (sgen_client_metadata_for_object (obj), is_weak);
445
446         copy = obj;
447         ctx->ops->copy_or_mark_object (&copy, ctx->queue);
448         SGEN_ASSERT (0, copy, "Why couldn't we copy the object?");
449         /* Update link if object was moved. */
450         return MONO_GC_HANDLE_OBJECT_POINTER (copy, is_weak);
451 }
452
453 /* LOCKING: requires that the GC lock is held */
454 void
455 sgen_null_link_in_range (int generation, ScanCopyContext ctx, gboolean track)
456 {
457         sgen_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if_necessary, &ctx);
458 }
459
460 typedef struct {
461         SgenObjectPredicateFunc predicate;
462         gpointer data;
463 } WeakLinkAlivePredicateClosure;
464
465 static gpointer
466 null_link_if (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user)
467 {
468         WeakLinkAlivePredicateClosure *closure = (WeakLinkAlivePredicateClosure *)user;
469         GCObject *obj;
470
471         if (!MONO_GC_HANDLE_VALID (hidden))
472                 return hidden;
473
474         obj = (GCObject *)MONO_GC_REVEAL_POINTER (hidden, MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type));
475         SGEN_ASSERT (0, obj, "Why is the hidden pointer NULL?");
476
477         if (object_older_than (obj, max_generation))
478                 return hidden;
479
480         if (closure->predicate (obj, closure->data))
481                 return MONO_GC_HANDLE_METADATA_POINTER (sgen_client_default_metadata (), GC_HANDLE_TYPE_IS_WEAK (handle_type));
482
483         return hidden;
484 }
485
486 /* LOCKING: requires that the GC lock is held */
487 void
488 sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation, gboolean track)
489 {
490         WeakLinkAlivePredicateClosure closure = { predicate, data };
491         sgen_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if, &closure);
492 }
493
494 void
495 sgen_init_gchandles (void)
496 {
497 #ifdef HEAVY_STATISTICS
498         mono_counters_register ("GC handles allocated", MONO_COUNTER_GC | MONO_COUNTER_UINT, (void *)&stat_gc_handles_allocated);
499         mono_counters_register ("max GC handles allocated", MONO_COUNTER_GC | MONO_COUNTER_UINT, (void *)&stat_gc_handles_max_allocated);
500 #endif
501 }
502
503 #endif