[2020-02] Fix leak in assembly-specific dllmap lookups (#21053)
[mono-project.git] / mono / metadata / null-gc-handles.c
blob6ba1c0fe7f99dc1b9f1db91d4bf767c9692be275
1 /* Borrowed from ./boehm-gc.c */
3 /* GC Handles */
5 #include "config.h"
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>
15 #ifdef HAVE_NULL_GC
17 #define HIDE_POINTER(obj) (obj)
18 #define REVEAL_POINTER(obj) (obj)
20 #define GC_call_with_alloc_lock(fnptr,arg) ((fnptr)((arg)))
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)
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;
37 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
39 /* weak and weak-track arrays will be allocated in malloc memory
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)
48 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
50 void
51 null_gc_handles_init (void)
53 mono_os_mutex_init_recursive (&handle_section);
56 static void
57 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
59 /* libgc requires that we use HIDE_POINTER... */
60 *link_addr = (void*)HIDE_POINTER (obj);
63 static void
64 mono_gc_weak_link_remove (void **link_addr, gboolean track)
66 *link_addr = NULL;
69 static gpointer
70 reveal_link (gpointer link_addr)
72 void **link_a = (void **)link_addr;
73 return REVEAL_POINTER (*link_a);
76 static MonoObject *
77 mono_gc_weak_link_get (void **link_addr)
79 MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
80 if (obj == (MonoObject *) -1)
81 return NULL;
82 return obj;
85 static gboolean
86 slot_occupied (HandleData *handles, guint slot) {
87 return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
90 static void
91 vacate_slot (HandleData *handles, guint slot) {
92 handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
95 static void
96 occupy_slot (HandleData *handles, guint slot) {
97 handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
100 static int
101 find_first_unset (guint32 bitmap)
103 int i;
104 for (i = 0; i < 32; ++i) {
105 if (!(bitmap & (1 << i)))
106 return i;
108 return -1;
111 static void
112 handle_data_alloc_entries (HandleData *handles)
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 Handle Table (Null)");
121 handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
124 static gint
125 handle_data_next_unset (HandleData *handles)
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]);
134 return -1;
137 static gint
138 handle_data_first_unset (HandleData *handles)
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]);
147 return -1;
150 /* Returns the index of the current slot in the bitmap. */
151 static void
152 handle_data_grow (HandleData *handles, gboolean track)
154 guint32 *new_bitmap;
155 guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
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;
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]);
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 Handle Table (Null)");
187 mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
188 mono_gc_free_fixed (handles->entries);
189 handles->entries = entries;
191 handles->slot_hint = handles->size / BITMAP_SIZE;
192 handles->size = new_size;
195 static guint32
196 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
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;
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_internal (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;
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;
232 * mono_gchandle_new:
233 * \param obj managed object to get a handle for
234 * \param pinned whether the object should be pinned
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.
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.
245 * \returns a handle that can be used to access the object from
246 * unmanaged code.
248 guint32
249 mono_gchandle_new_internal (MonoObject *obj, gboolean pinned)
251 return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
255 * mono_gchandle_new_weakref_internal:
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.
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_internal the object can be reclaimed by the
262 * garbage collector. In this case the value of the GCHandle will be
263 * set to zero.
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.
272 * \returns a handle that can be used to access the object from
273 * unmanaged code.
275 guint32
276 mono_gchandle_new_weakref_internal (MonoObject *obj, gboolean track_resurrection)
278 return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
282 * mono_gchandle_get_target:
283 * \param gchandle a GCHandle's handle.
285 * The handle was previously created by calling \c mono_gchandle_new_internal or
286 * \c mono_gchandle_new_weakref.
288 * \returns A pointer to the \c MonoObject* represented by the handle or
289 * NULL for a collected object if using a weakref handle.
291 MonoObject*
292 mono_gchandle_get_target_internal (guint32 gchandle)
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;
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];
308 } else {
309 /* print a warning? */
311 unlock_handles (handles);
312 /*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
313 return obj;
316 void
317 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
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;
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_internal (obj) : mono_domain_get ())->domain_id;
335 } else {
336 handles->entries [slot] = obj;
338 } else {
339 /* print a warning? */
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);
346 * mono_gchandle_is_in_domain:
347 * \param gchandle a GCHandle's handle.
348 * \param domain An application domain.
350 * Use this function to determine if the \p gchandle points to an
351 * object allocated in the specified \p domain.
353 * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
355 gboolean
356 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
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;
363 if (type >= HANDLE_TYPE_MAX)
364 return FALSE;
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);
378 } else {
379 /* print a warning? */
381 unlock_handles (handles);
382 return result;
386 * mono_gchandle_free:
387 * \param gchandle a GCHandle's handle.
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.
393 void
394 mono_gchandle_free_internal (guint32 gchandle)
396 if (!gchandle)
397 return;
399 guint slot = MONO_GC_HANDLE_SLOT (gchandle);
400 guint type = MONO_GC_HANDLE_TYPE (gchandle);
401 HandleData *handles = &gc_handles [type];
402 if (type >= HANDLE_TYPE_MAX)
403 return;
405 lock_handles (handles);
406 if (slot < handles->size && slot_occupied (handles, slot)) {
407 if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
408 if (handles->entries [slot])
409 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
410 } else {
411 handles->entries [slot] = NULL;
413 vacate_slot (handles, slot);
414 } else {
415 /* print a warning? */
417 #ifndef DISABLE_PERFCOUNTERS
418 mono_atomic_dec_i32 (&mono_perfcounters->gc_num_handles);
419 #endif
420 /*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
421 unlock_handles (handles);
422 mono_profiler_gc_handle (MONO_PROFILER_GC_HANDLE_DESTROYED, handles->type, gchandle, NULL);
426 * mono_gchandle_free_domain:
427 * \param domain domain that is unloading
429 * Function used internally to cleanup any GC handle for objects belonging
430 * to the specified domain during appdomain unload.
432 void
433 mono_gchandle_free_domain (MonoDomain *domain)
435 guint type;
437 for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
438 guint slot;
439 HandleData *handles = &gc_handles [type];
440 lock_handles (handles);
441 for (slot = 0; slot < handles->size; ++slot) {
442 if (!slot_occupied (handles, slot))
443 continue;
444 if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
445 if (domain->domain_id == handles->domain_ids [slot]) {
446 vacate_slot (handles, slot);
447 if (handles->entries [slot])
448 mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
450 } else {
451 if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
452 vacate_slot (handles, slot);
453 handles->entries [slot] = NULL;
457 unlock_handles (handles);
461 #else
463 MONO_EMPTY_SOURCE_FILE (null_gc_handles);
464 #endif /* HAVE_NULL_GC */