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