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