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
null_gc_handles_init(void)51 null_gc_handles_init (void)
52 {
53 mono_os_mutex_init_recursive (&handle_section);
54 }
55
56 static void
mono_gc_weak_link_add(void ** link_addr,MonoObject * obj,gboolean track)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
mono_gc_weak_link_remove(void ** link_addr,gboolean track)64 mono_gc_weak_link_remove (void **link_addr, gboolean track)
65 {
66 *link_addr = NULL;
67 }
68
69 static gpointer
reveal_link(gpointer link_addr)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 *
mono_gc_weak_link_get(void ** link_addr)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
slot_occupied(HandleData * handles,guint slot)86 slot_occupied (HandleData *handles, guint slot) {
87 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
88 }
89
90 static inline void
vacate_slot(HandleData * handles,guint slot)91 vacate_slot (HandleData *handles, guint slot) {
92 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
93 }
94
95 static inline void
occupy_slot(HandleData * handles,guint slot)96 occupy_slot (HandleData *handles, guint slot) {
97 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
98 }
99
100 static int
find_first_unset(guint32 bitmap)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
handle_data_alloc_entries(HandleData * handles)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
handle_data_next_unset(HandleData * handles)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
handle_data_first_unset(HandleData * handles)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
handle_data_grow(HandleData * handles,gboolean track)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
alloc_handle(HandleData * handles,MonoObject * obj,gboolean track)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 mono_atomic_inc_i32 (&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
mono_gchandle_new(MonoObject * obj,gboolean pinned)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
mono_gchandle_new_weakref(MonoObject * obj,gboolean track_resurrection)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*
mono_gchandle_get_target(guint32 gchandle)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
mono_gchandle_set_target(guint32 gchandle,MonoObject * obj)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
mono_gchandle_is_in_domain(guint32 gchandle,MonoDomain * domain)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
mono_gchandle_free(guint32 gchandle)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 mono_atomic_dec_i32 (&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
mono_gchandle_free_domain(MonoDomain * domain)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 */
462