Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / metadata / null-gc-handles.c
1 /* Borrowed from ./boehm-gc.c */
2
3 /* GC Handles */
4
5 #include "config.h"
6
7 #include <glib.h>
8 #include <mono/metadata/mono-gc.h>
9 #include <mono/metadata/gc-internals.h>
10 #include <mono/metadata/profiler-private.h>
11 #include <mono/utils/mono-compiler.h>
12 #include <mono/metadata/null-gc-handles.h>
13
14
15 #ifdef HAVE_NULL_GC
16
17 #define HIDE_POINTER(obj) (obj)
18 #define REVEAL_POINTER(obj) (obj)
19
20 #define GC_call_with_alloc_lock(fnptr,arg) ((fnptr)((arg)))
21
22 static mono_mutex_t handle_section;
23 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
24 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
25
26 typedef struct {
27         guint32  *bitmap;
28         gpointer *entries;
29         guint32   size;
30         guint8    type;
31         guint     slot_hint : 24; /* starting slot for search in bitmap */
32         /* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
33         /* we alloc this only for weak refs, since we can get the domain directly in the other cases */
34         guint16  *domain_ids;
35 } HandleData;
36
37 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
38
39 /* weak and weak-track arrays will be allocated in malloc memory 
40  */
41 static HandleData gc_handles [] = {
42         EMPTY_HANDLE_DATA (HANDLE_WEAK),
43         EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
44         EMPTY_HANDLE_DATA (HANDLE_NORMAL),
45         EMPTY_HANDLE_DATA (HANDLE_PINNED)
46 };
47
48 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
49
50 void
51 null_gc_handles_init (void)
52 {
53         mono_os_mutex_init_recursive (&handle_section);
54 }
55
56 static void
57 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
58 {
59         /* libgc requires that we use HIDE_POINTER... */
60         *link_addr = (void*)HIDE_POINTER (obj);
61 }
62
63 static void
64 mono_gc_weak_link_remove (void **link_addr, gboolean track)
65 {
66         *link_addr = NULL;
67 }
68
69 static gpointer
70 reveal_link (gpointer link_addr)
71 {
72         void **link_a = (void **)link_addr;
73         return REVEAL_POINTER (*link_a);
74 }
75
76 static MonoObject *
77 mono_gc_weak_link_get (void **link_addr)
78 {
79         MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
80         if (obj == (MonoObject *) -1)
81                 return NULL;
82         return obj;
83 }
84
85 static inline gboolean
86 slot_occupied (HandleData *handles, guint slot) {
87         return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
88 }
89
90 static inline void
91 vacate_slot (HandleData *handles, guint slot) {
92         handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
93 }
94
95 static inline void
96 occupy_slot (HandleData *handles, guint slot) {
97         handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
98 }
99
100 static int
101 find_first_unset (guint32 bitmap)
102 {
103         int i;
104         for (i = 0; i < 32; ++i) {
105                 if (!(bitmap & (1 << i)))
106                         return i;
107         }
108         return -1;
109 }
110
111 static void
112 handle_data_alloc_entries (HandleData *handles)
113 {
114         handles->size = 32;
115         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
116                 handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
117                 handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
118         } else {
119                 handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
120         }
121         handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
122 }
123
124 static gint
125 handle_data_next_unset (HandleData *handles)
126 {
127         gint slot;
128         for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
129                 if (handles->bitmap [slot] == 0xffffffff)
130                         continue;
131                 handles->slot_hint = slot;
132                 return find_first_unset (handles->bitmap [slot]);
133         }
134         return -1;
135 }
136
137 static gint
138 handle_data_first_unset (HandleData *handles)
139 {
140         gint slot;
141         for (slot = 0; slot < handles->slot_hint; ++slot) {
142                 if (handles->bitmap [slot] == 0xffffffff)
143                         continue;
144                 handles->slot_hint = slot;
145                 return find_first_unset (handles->bitmap [slot]);
146         }
147         return -1;
148 }
149
150 /* Returns the index of the current slot in the bitmap. */
151 static void
152 handle_data_grow (HandleData *handles, gboolean track)
153 {
154         guint32 *new_bitmap;
155         guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
156
157         /* resize and copy the bitmap */
158         new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
159         memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
160         g_free (handles->bitmap);
161         handles->bitmap = new_bitmap;
162
163         /* resize and copy the entries */
164         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
165                 gpointer *entries;
166                 guint16 *domain_ids;
167                 gint i;
168                 domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
169                 entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
170                 memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
171                 for (i = 0; i < handles->size; ++i) {
172                         MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
173                         if (obj) {
174                                 mono_gc_weak_link_add (&(entries [i]), obj, track);
175                                 mono_gc_weak_link_remove (&(handles->entries [i]), track);
176                         } else {
177                                 g_assert (!handles->entries [i]);
178                         }
179                 }
180                 g_free (handles->entries);
181                 g_free (handles->domain_ids);
182                 handles->entries = entries;
183                 handles->domain_ids = domain_ids;
184         } else {
185                 gpointer *entries;
186                 entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
187                 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
188                 mono_gc_free_fixed (handles->entries);
189                 handles->entries = entries;
190         }
191         handles->slot_hint = handles->size / BITMAP_SIZE;
192         handles->size = new_size;
193 }
194
195 static guint32
196 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
197 {
198         gint slot, i;
199         guint32 res;
200         lock_handles (handles);
201         if (!handles->size)
202                 handle_data_alloc_entries (handles);
203         i = handle_data_next_unset (handles);
204         if (i == -1 && handles->slot_hint != 0)
205                 i = handle_data_first_unset (handles);
206         if (i == -1) {
207                 handle_data_grow (handles, track);
208                 i = 0;
209         }
210         slot = handles->slot_hint * BITMAP_SIZE + i;
211         occupy_slot (handles, slot);
212         handles->entries [slot] = NULL;
213         if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
214                 /*FIXME, what to use when obj == null?*/
215                 handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
216                 if (obj)
217                         mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
218         } else {
219                 handles->entries [slot] = obj;
220         }
221
222 #ifndef DISABLE_PERFCOUNTERS
223         InterlockedIncrement (&mono_perfcounters->gc_num_handles);
224 #endif
225         unlock_handles (handles);
226         res = MONO_GC_HANDLE (slot, handles->type);
227         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_CREATED, handles->type, res, obj);
228         return res;
229 }
230
231 /**
232  * mono_gchandle_new:
233  * \param obj managed object to get a handle for
234  * \param pinned whether the object should be pinned
235  *
236  * This returns a handle that wraps the object, this is used to keep a
237  * reference to a managed object from the unmanaged world and preventing the
238  * object from being disposed.
239  * 
240  * If \p pinned is false the address of the object can not be obtained, if it is
241  * true the address of the object can be obtained.  This will also pin the
242  * object so it will not be possible by a moving garbage collector to move the
243  * object. 
244  * 
245  * \returns a handle that can be used to access the object from
246  * unmanaged code.
247  */
248 guint32
249 mono_gchandle_new (MonoObject *obj, gboolean pinned)
250 {
251         return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
252 }
253
254 /**
255  * mono_gchandle_new_weakref:
256  * \param obj managed object to get a handle for
257  * \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.
258  *
259  * This returns a weak handle that wraps the object, this is used to
260  * keep a reference to a managed object from the unmanaged world.
261  * Unlike the \c mono_gchandle_new the object can be reclaimed by the
262  * garbage collector.  In this case the value of the GCHandle will be
263  * set to zero.
264  * 
265  * If \p track_resurrection is TRUE the object will be tracked through
266  * finalization and if the object is resurrected during the execution
267  * of the finalizer, then the returned weakref will continue to hold
268  * a reference to the object.   If \p track_resurrection is FALSE, then
269  * the weak reference's target will become NULL as soon as the object
270  * is passed on to the finalizer.
271  * 
272  * \returns a handle that can be used to access the object from
273  * unmanaged code.
274  */
275 guint32
276 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
277 {
278         return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
279 }
280
281 /**
282  * mono_gchandle_get_target:
283  * \param gchandle a GCHandle's handle.
284  *
285  * The handle was previously created by calling \c mono_gchandle_new or
286  * \c mono_gchandle_new_weakref.
287  *
288  * \returns A pointer to the \c MonoObject* represented by the handle or
289  * NULL for a collected object if using a weakref handle.
290  */
291 MonoObject*
292 mono_gchandle_get_target (guint32 gchandle)
293 {
294         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
295         guint type = MONO_GC_HANDLE_TYPE (gchandle);
296         HandleData *handles = &gc_handles [type];
297         MonoObject *obj = NULL;
298         if (type >= HANDLE_TYPE_MAX)
299                 return NULL;
300
301         lock_handles (handles);
302         if (slot < handles->size && slot_occupied (handles, slot)) {
303                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
304                         obj = mono_gc_weak_link_get (&handles->entries [slot]);
305                 } else {
306                         obj = (MonoObject *)handles->entries [slot];
307                 }
308         } else {
309                 /* print a warning? */
310         }
311         unlock_handles (handles);
312         /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
313         return obj;
314 }
315
316 void
317 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
318 {
319         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
320         guint type = MONO_GC_HANDLE_TYPE (gchandle);
321         HandleData *handles = &gc_handles [type];
322         MonoObject *old_obj = NULL;
323
324         g_assert (type < HANDLE_TYPE_MAX);
325         lock_handles (handles);
326         if (slot < handles->size && slot_occupied (handles, slot)) {
327                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
328                         old_obj = (MonoObject *)handles->entries [slot];
329                         if (handles->entries [slot])
330                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
331                         if (obj)
332                                 mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
333                         /*FIXME, what to use when obj == null?*/
334                         handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
335                 } else {
336                         handles->entries [slot] = obj;
337                 }
338         } else {
339                 /* print a warning? */
340         }
341         /*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
342         unlock_handles (handles);
343 }
344
345 /**
346  * mono_gchandle_is_in_domain:
347  * \param gchandle a GCHandle's handle.
348  * \param domain An application domain.
349  *
350  * Use this function to determine if the \p gchandle points to an
351  * object allocated in the specified \p domain.
352  *
353  * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
354  */
355 gboolean
356 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
357 {
358         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
359         guint type = MONO_GC_HANDLE_TYPE (gchandle);
360         HandleData *handles = &gc_handles [type];
361         gboolean result = FALSE;
362
363         if (type >= HANDLE_TYPE_MAX)
364                 return FALSE;
365
366         lock_handles (handles);
367         if (slot < handles->size && slot_occupied (handles, slot)) {
368                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
369                         result = domain->domain_id == handles->domain_ids [slot];
370                 } else {
371                         MonoObject *obj;
372                         obj = (MonoObject *)handles->entries [slot];
373                         if (obj == NULL)
374                                 result = TRUE;
375                         else
376                                 result = domain == mono_object_domain (obj);
377                 }
378         } else {
379                 /* print a warning? */
380         }
381         unlock_handles (handles);
382         return result;
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         guint slot = MONO_GC_HANDLE_SLOT (gchandle);
397         guint type = MONO_GC_HANDLE_TYPE (gchandle);
398         HandleData *handles = &gc_handles [type];
399         if (type >= HANDLE_TYPE_MAX)
400                 return;
401
402         lock_handles (handles);
403         if (slot < handles->size && slot_occupied (handles, slot)) {
404                 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
405                         if (handles->entries [slot])
406                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
407                 } else {
408                         handles->entries [slot] = NULL;
409                 }
410                 vacate_slot (handles, slot);
411         } else {
412                 /* print a warning? */
413         }
414 #ifndef DISABLE_PERFCOUNTERS
415         InterlockedDecrement (&mono_perfcounters->gc_num_handles);
416 #endif
417         /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
418         unlock_handles (handles);
419         mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
420 }
421
422 /**
423  * mono_gchandle_free_domain:
424  * \param domain domain that is unloading
425  *
426  * Function used internally to cleanup any GC handle for objects belonging
427  * to the specified domain during appdomain unload.
428  */
429 void
430 mono_gchandle_free_domain (MonoDomain *domain)
431 {
432         guint type;
433
434         for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
435                 guint slot;
436                 HandleData *handles = &gc_handles [type];
437                 lock_handles (handles);
438                 for (slot = 0; slot < handles->size; ++slot) {
439                         if (!slot_occupied (handles, slot))
440                                 continue;
441                         if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
442                                 if (domain->domain_id == handles->domain_ids [slot]) {
443                                         vacate_slot (handles, slot);
444                                         if (handles->entries [slot])
445                                                 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
446                                 }
447                         } else {
448                                 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
449                                         vacate_slot (handles, slot);
450                                         handles->entries [slot] = NULL;
451                                 }
452                         }
453                 }
454                 unlock_handles (handles);
455         }
456
457 }
458 #else
459
460 MONO_EMPTY_SOURCE_FILE (null_gc_handles);
461 #endif /* HAVE_NULL_GC */