555d5bf483f68db8dc8f02f59fec6d389f129f5b
[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  * \param obj managed object to get a handle for
208  * \param pinned whether the object should be pinned
209  * This returns a handle that wraps the object, this is used to keep a
210  * reference to a managed object from the unmanaged world and preventing the
211  * object from being disposed.
212  * 
213  * If \p pinned is false the address of the object can not be obtained, if it is
214  * true the address of the object can be obtained.  This will also pin the
215  * object so it will not be possible by a moving garbage collector to move the
216  * object. 
217  * 
218  * \returns a handle that can be used to access the object from unmanaged code.
219  */
220 guint32
221 mono_gchandle_new (GCObject *obj, gboolean pinned)
222 {
223         return alloc_handle (gc_handles_for_type (pinned ? HANDLE_PINNED : HANDLE_NORMAL), obj, FALSE);
224 }
225
226 /**
227  * mono_gchandle_new_weakref:
228  * \param obj managed object to get a handle for
229  * \param 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.
230  *
231  * This returns a weak handle that wraps the object, this is used to
232  * keep a reference to a managed object from the unmanaged world.
233  * Unlike the \c mono_gchandle_new the object can be reclaimed by the
234  * garbage collector.  In this case the value of the GCHandle will be
235  * set to zero.
236  * 
237  * If \p track_resurrection is TRUE the object will be tracked through
238  * finalization and if the object is resurrected during the execution
239  * of the finalizer, then the returned weakref will continue to hold
240  * a reference to the object.   If \p track_resurrection is FALSE, then
241  * the weak reference's target will become NULL as soon as the object
242  * is passed on to the finalizer.
243  * 
244  * \returns a handle that can be used to access the object from
245  * unmanaged code.
246  */
247 guint32
248 mono_gchandle_new_weakref (GCObject *obj, gboolean track_resurrection)
249 {
250         return alloc_handle (gc_handles_for_type (track_resurrection ? HANDLE_WEAK_TRACK : HANDLE_WEAK), obj, track_resurrection);
251 }
252
253 static GCObject *
254 link_get (volatile gpointer *link_addr, gboolean is_weak)
255 {
256         void *volatile *link_addr_volatile;
257         void *ptr;
258         GCObject *obj;
259 retry:
260         link_addr_volatile = link_addr;
261         ptr = (void*)*link_addr_volatile;
262         /*
263          * At this point we have a hidden pointer.  If the GC runs
264          * here, it will not recognize the hidden pointer as a
265          * reference, and if the object behind it is not referenced
266          * elsewhere, it will be freed.  Once the world is restarted
267          * we reveal the pointer, giving us a pointer to a freed
268          * object.  To make sure we don't return it, we load the
269          * hidden pointer again.  If it's still the same, we can be
270          * sure the object reference is valid.
271          */
272         if (ptr && MONO_GC_HANDLE_IS_OBJECT_POINTER (ptr))
273                 obj = (GCObject *)MONO_GC_REVEAL_POINTER (ptr, is_weak);
274         else
275                 return NULL;
276
277         /* Note [dummy use]:
278          *
279          * If a GC happens here, obj needs to be on the stack or in a
280          * register, so we need to prevent this from being reordered
281          * wrt the check.
282          */
283         sgen_dummy_use (obj);
284         mono_memory_barrier ();
285
286         if (is_weak)
287                 sgen_client_ensure_weak_gchandles_accessible ();
288
289         if ((void*)*link_addr_volatile != ptr)
290                 goto retry;
291
292         return obj;
293 }
294
295 /**
296  * mono_gchandle_get_target:
297  * \param gchandle a GCHandle's handle.
298  *
299  * The handle was previously created by calling \c mono_gchandle_new or
300  * \c mono_gchandle_new_weakref. 
301  *
302  * \returns a pointer to the \c MonoObject* represented by the handle or
303  * NULL for a collected object if using a weakref handle.
304  */
305 GCObject*
306 mono_gchandle_get_target (guint32 gchandle)
307 {
308         guint index = MONO_GC_HANDLE_SLOT (gchandle);
309         GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
310         HandleData *handles = gc_handles_for_type (type);
311         /* Invalid handles are possible; accessing one should produce NULL. (#34276) */
312         if (!handles)
313                 return NULL;
314         return link_get (sgen_array_list_get_slot (&handles->entries_array, index), MONO_GC_HANDLE_TYPE_IS_WEAK (type));
315 }
316
317 void
318 sgen_gchandle_set_target (guint32 gchandle, GCObject *obj)
319 {
320         guint32 index = MONO_GC_HANDLE_SLOT (gchandle);
321         GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
322         HandleData *handles = gc_handles_for_type (type);
323         volatile gpointer *slot;
324         gpointer entry;
325
326         if (!handles)
327                 return;
328
329         slot = sgen_array_list_get_slot (&handles->entries_array, index);
330
331         do {
332                 entry = *slot;
333                 SGEN_ASSERT (0, MONO_GC_HANDLE_OCCUPIED (entry), "Why are we setting the target on an unoccupied slot?");
334         } while (!try_set_slot (slot, obj, entry, (GCHandleType)handles->type));
335 }
336
337 static gpointer
338 mono_gchandle_slot_metadata (volatile gpointer *slot, gboolean is_weak)
339 {
340         gpointer entry;
341         gpointer metadata;
342 retry:
343         entry = *slot;
344         if (!MONO_GC_HANDLE_OCCUPIED (entry))
345                 return NULL;
346         if (MONO_GC_HANDLE_IS_OBJECT_POINTER (entry)) {
347                 GCObject *obj = (GCObject *)MONO_GC_REVEAL_POINTER (entry, is_weak);
348                 /* See note [dummy use]. */
349                 sgen_dummy_use (obj);
350                 /*
351                  * FIXME: The compiler could technically not carry a reference to obj around
352                  * at this point and recompute it later, in which case we would still use
353                  * it.
354                  */
355                 if (*slot != entry)
356                         goto retry;
357                 return sgen_client_metadata_for_object (obj);
358         }
359         metadata = MONO_GC_REVEAL_POINTER (entry, is_weak);
360         /* See note [dummy use]. */
361         sgen_dummy_use (metadata);
362         if (*slot != entry)
363                 goto retry;
364         return metadata;
365 }
366
367 gpointer
368 sgen_gchandle_get_metadata (guint32 gchandle)
369 {
370         guint32 index = MONO_GC_HANDLE_SLOT (gchandle);
371         GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
372         HandleData *handles = gc_handles_for_type (type);
373         volatile gpointer *slot;
374
375         if (!handles)
376                 return NULL;
377         if (index >= handles->entries_array.capacity)
378                 return NULL;
379
380         slot = sgen_array_list_get_slot (&handles->entries_array, index);
381
382         return mono_gchandle_slot_metadata (slot, MONO_GC_HANDLE_TYPE_IS_WEAK (type));
383 }
384
385 /**
386  * mono_gchandle_free:
387  * \param gchandle a GCHandle's handle.
388  *
389  * Frees the \p gchandle handle.  If there are no outstanding
390  * references, the garbage collector can reclaim the memory of the
391  * object wrapped. 
392  */
393 void
394 mono_gchandle_free (guint32 gchandle)
395 {
396         guint32 index = MONO_GC_HANDLE_SLOT (gchandle);
397         GCHandleType type = MONO_GC_HANDLE_TYPE (gchandle);
398         HandleData *handles = gc_handles_for_type (type);
399         volatile gpointer *slot;
400         gpointer entry;
401         if (!handles)
402                 return;
403
404         slot = sgen_array_list_get_slot (&handles->entries_array, index);
405         entry = *slot;
406
407         if (index < handles->entries_array.capacity && MONO_GC_HANDLE_OCCUPIED (entry)) {
408                 *slot = NULL;
409                 protocol_gchandle_update (handles->type, (gpointer)slot, entry, NULL);
410                 HEAVY_STAT (InterlockedDecrement ((volatile gint32 *)&stat_gc_handles_allocated));
411         } else {
412                 /* print a warning? */
413         }
414         sgen_client_gchandle_destroyed (handles->type, gchandle);
415 }
416
417 /*
418  * Returns whether to remove the link from its hash.
419  */
420 static gpointer
421 null_link_if_necessary (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user)
422 {
423         const gboolean is_weak = GC_HANDLE_TYPE_IS_WEAK (handle_type);
424         ScanCopyContext *ctx = (ScanCopyContext *)user;
425         GCObject *obj;
426         GCObject *copy;
427
428         if (!MONO_GC_HANDLE_VALID (hidden))
429                 return hidden;
430
431         obj = (GCObject *)MONO_GC_REVEAL_POINTER (hidden, MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type));
432         SGEN_ASSERT (0, obj, "Why is the hidden pointer NULL?");
433
434         if (object_older_than (obj, max_generation))
435                 return hidden;
436
437         if (major_collector.is_object_live (obj))
438                 return hidden;
439
440         /* Clear link if object is ready for finalization. This check may be redundant wrt is_object_live(). */
441         if (sgen_gc_is_object_ready_for_finalization (obj))
442                 return MONO_GC_HANDLE_METADATA_POINTER (sgen_client_metadata_for_object (obj), is_weak);
443
444         copy = obj;
445         ctx->ops->copy_or_mark_object (&copy, ctx->queue);
446         SGEN_ASSERT (0, copy, "Why couldn't we copy the object?");
447         /* Update link if object was moved. */
448         return MONO_GC_HANDLE_OBJECT_POINTER (copy, is_weak);
449 }
450
451 /* LOCKING: requires that the GC lock is held */
452 void
453 sgen_null_link_in_range (int generation, ScanCopyContext ctx, gboolean track)
454 {
455         sgen_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if_necessary, &ctx);
456 }
457
458 typedef struct {
459         SgenObjectPredicateFunc predicate;
460         gpointer data;
461 } WeakLinkAlivePredicateClosure;
462
463 static gpointer
464 null_link_if (gpointer hidden, GCHandleType handle_type, int max_generation, gpointer user)
465 {
466         WeakLinkAlivePredicateClosure *closure = (WeakLinkAlivePredicateClosure *)user;
467         GCObject *obj;
468
469         if (!MONO_GC_HANDLE_VALID (hidden))
470                 return hidden;
471
472         obj = (GCObject *)MONO_GC_REVEAL_POINTER (hidden, MONO_GC_HANDLE_TYPE_IS_WEAK (handle_type));
473         SGEN_ASSERT (0, obj, "Why is the hidden pointer NULL?");
474
475         if (object_older_than (obj, max_generation))
476                 return hidden;
477
478         if (closure->predicate (obj, closure->data))
479                 return MONO_GC_HANDLE_METADATA_POINTER (sgen_client_default_metadata (), GC_HANDLE_TYPE_IS_WEAK (handle_type));
480
481         return hidden;
482 }
483
484 /* LOCKING: requires that the GC lock is held */
485 void
486 sgen_null_links_if (SgenObjectPredicateFunc predicate, void *data, int generation, gboolean track)
487 {
488         WeakLinkAlivePredicateClosure closure = { predicate, data };
489         sgen_gchandle_iterate (track ? HANDLE_WEAK_TRACK : HANDLE_WEAK, generation, null_link_if, &closure);
490 }
491
492 void
493 sgen_init_gchandles (void)
494 {
495 #ifdef HEAVY_STATISTICS
496         mono_counters_register ("GC handles allocated", MONO_COUNTER_GC | MONO_COUNTER_UINT, (void *)&stat_gc_handles_allocated);
497         mono_counters_register ("max GC handles allocated", MONO_COUNTER_GC | MONO_COUNTER_UINT, (void *)&stat_gc_handles_max_allocated);
498 #endif
499 }
500
501 #endif