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