1 /**
2  * \file
3  * GC implementation using either the installed or included Boehm GC.
4  *
5  * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
6  * Copyright 2004-2011 Novell, Inc (http://www.novell.com)
7  * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
8  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9  */
10 
11 #include "config.h"
12 
13 #include <string.h>
14 
15 #define GC_I_HIDE_POINTERS
16 #include <mono/metadata/gc-internals.h>
17 #include <mono/metadata/mono-gc.h>
18 #include <mono/metadata/profiler-private.h>
19 #include <mono/metadata/class-internals.h>
20 #include <mono/metadata/method-builder.h>
21 #include <mono/metadata/opcodes.h>
22 #include <mono/metadata/domain-internals.h>
23 #include <mono/metadata/metadata-internals.h>
24 #include <mono/metadata/marshal.h>
25 #include <mono/metadata/runtime.h>
26 #include <mono/metadata/handle.h>
27 #include <mono/metadata/sgen-toggleref.h>
28 #include <mono/metadata/w32handle.h>
29 #include <mono/utils/atomic.h>
30 #include <mono/utils/mono-logger-internals.h>
31 #include <mono/utils/mono-memory-model.h>
32 #include <mono/utils/mono-time.h>
33 #include <mono/utils/mono-threads.h>
34 #include <mono/utils/dtrace.h>
35 #include <mono/utils/gc_wrapper.h>
36 #include <mono/utils/mono-os-mutex.h>
37 #include <mono/utils/mono-counters.h>
38 #include <mono/utils/mono-compiler.h>
39 #include <mono/utils/unlocked.h>
40 
41 #if HAVE_BOEHM_GC
42 
43 #undef TRUE
44 #undef FALSE
45 #define THREAD_LOCAL_ALLOC 1
46 #include "private/pthread_support.h"
47 
48 #if defined(HOST_DARWIN) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
49 void *pthread_get_stackaddr_np(pthread_t);
50 #endif
51 
52 #define GC_NO_DESCRIPTOR ((gpointer)(0 | GC_DS_LENGTH))
53 /*Boehm max heap cannot be smaller than 16MB*/
54 #define MIN_BOEHM_MAX_HEAP_SIZE_IN_MB 16
55 #define MIN_BOEHM_MAX_HEAP_SIZE (MIN_BOEHM_MAX_HEAP_SIZE_IN_MB << 20)
56 
57 static gboolean gc_initialized = FALSE;
58 static mono_mutex_t mono_gc_lock;
59 
60 typedef void (*GC_push_other_roots_proc)(void);
61 
62 static GC_push_other_roots_proc default_push_other_roots;
63 static GHashTable *roots;
64 
65 static void
66 mono_push_other_roots(void);
67 
68 static void
69 register_test_toggleref_callback (void);
70 
71 #define BOEHM_GC_BIT_FINALIZER_AWARE 1
72 static MonoGCFinalizerCallbacks fin_callbacks;
73 
74 /* GC Handles */
75 
76 static mono_mutex_t handle_section;
77 #define lock_handles(handles) mono_os_mutex_lock (&handle_section)
78 #define unlock_handles(handles) mono_os_mutex_unlock (&handle_section)
79 
80 typedef struct {
81 	guint32  *bitmap;
82 	gpointer *entries;
83 	guint32   size;
84 	guint8    type;
85 	guint     slot_hint : 24; /* starting slot for search in bitmap */
86 	/* 2^16 appdomains should be enough for everyone (though I know I'll regret this in 20 years) */
87 	/* we alloc this only for weak refs, since we can get the domain directly in the other cases */
88 	guint16  *domain_ids;
89 } HandleData;
90 
91 #define EMPTY_HANDLE_DATA(type) {NULL, NULL, 0, (type), 0, NULL}
92 
93 /* weak and weak-track arrays will be allocated in malloc memory
94  */
95 static HandleData gc_handles [] = {
96 	EMPTY_HANDLE_DATA (HANDLE_WEAK),
97 	EMPTY_HANDLE_DATA (HANDLE_WEAK_TRACK),
98 	EMPTY_HANDLE_DATA (HANDLE_NORMAL),
99 	EMPTY_HANDLE_DATA (HANDLE_PINNED)
100 };
101 
102 static void
mono_gc_warning(char * msg,GC_word arg)103 mono_gc_warning (char *msg, GC_word arg)
104 {
105 	mono_trace (G_LOG_LEVEL_WARNING, MONO_TRACE_GC, msg, (unsigned long)arg);
106 }
107 
108 static void on_gc_notification (GC_EventType event);
109 static void on_gc_heap_resize (size_t new_size);
110 
111 void
mono_gc_base_init(void)112 mono_gc_base_init (void)
113 {
114 	char *env;
115 
116 	if (gc_initialized)
117 		return;
118 
119 	mono_counters_init ();
120 
121 #ifndef HOST_WIN32
122 	mono_w32handle_init ();
123 #endif
124 
125 	/*
126 	 * Handle the case when we are called from a thread different from the main thread,
127 	 * confusing libgc.
128 	 * FIXME: Move this to libgc where it belongs.
129 	 *
130 	 * we used to do this only when running on valgrind,
131 	 * but it happens also in other setups.
132 	 */
133 #if defined(HAVE_PTHREAD_GETATTR_NP) && defined(HAVE_PTHREAD_ATTR_GETSTACK)
134 	{
135 		size_t size;
136 		void *sstart;
137 		pthread_attr_t attr;
138 		pthread_getattr_np (pthread_self (), &attr);
139 		pthread_attr_getstack (&attr, &sstart, &size);
140 		pthread_attr_destroy (&attr);
141 		/*g_print ("stackbottom pth is: %p\n", (char*)sstart + size);*/
142 		/* apparently with some linuxthreads implementations sstart can be NULL,
143 		 * fallback to the more imprecise method (bug# 78096).
144 		 */
145 		if (sstart) {
146 			GC_stackbottom = (char*)sstart + size;
147 		} else {
148 			int dummy;
149 			gsize stack_bottom = (gsize)&dummy;
150 			stack_bottom += 4095;
151 			stack_bottom &= ~4095;
152 			GC_stackbottom = (char*)stack_bottom;
153 		}
154 	}
155 #elif defined(HAVE_PTHREAD_GET_STACKSIZE_NP) && defined(HAVE_PTHREAD_GET_STACKADDR_NP)
156 		GC_stackbottom = (char*)pthread_get_stackaddr_np (pthread_self ());
157 #elif defined(__OpenBSD__)
158 #  include <pthread_np.h>
159 	{
160 		stack_t ss;
161 		int rslt;
162 
163 		rslt = pthread_stackseg_np(pthread_self(), &ss);
164 		g_assert (rslt == 0);
165 
166 		GC_stackbottom = (char*)ss.ss_sp;
167 	}
168 #else
169 	{
170 		int dummy;
171 		gsize stack_bottom = (gsize)&dummy;
172 		stack_bottom += 4095;
173 		stack_bottom &= ~4095;
174 		/*g_print ("stackbottom is: %p\n", (char*)stack_bottom);*/
175 		GC_stackbottom = (char*)stack_bottom;
176 	}
177 #endif
178 
179 	roots = g_hash_table_new (NULL, NULL);
180 	default_push_other_roots = GC_push_other_roots;
181 	GC_push_other_roots = mono_push_other_roots;
182 
183 #if !defined(HOST_ANDROID)
184 	/* If GC_no_dls is set to true, GC_find_limit is not called. This causes a seg fault on Android. */
185 	GC_no_dls = TRUE;
186 #endif
187 	{
188 		if ((env = g_getenv ("MONO_GC_DEBUG"))) {
189 			char **opts = g_strsplit (env, ",", -1);
190 			for (char **ptr = opts; ptr && *ptr; ptr ++) {
191 				char *opt = *ptr;
192 				if (!strcmp (opt, "do-not-finalize")) {
193 					mono_do_not_finalize = 1;
194 				} else if (!strcmp (opt, "log-finalizers")) {
195 					log_finalizers = 1;
196 				}
197 			}
198 			g_free (env);
199 		}
200 	}
201 
202 	GC_init ();
203 
204 	GC_set_warn_proc (mono_gc_warning);
205 	GC_finalize_on_demand = 1;
206 	GC_finalizer_notifier = mono_gc_finalize_notify;
207 
208 	GC_init_gcj_malloc (5, NULL);
209 	GC_allow_register_threads ();
210 
211 	if ((env = g_getenv ("MONO_GC_PARAMS"))) {
212 		char **ptr, **opts = g_strsplit (env, ",", -1);
213 		for (ptr = opts; *ptr; ++ptr) {
214 			char *opt = *ptr;
215 			if (g_str_has_prefix (opt, "max-heap-size=")) {
216 				size_t max_heap;
217 
218 				opt = strchr (opt, '=') + 1;
219 				if (*opt && mono_gc_parse_environment_string_extract_number (opt, &max_heap)) {
220 					if (max_heap < MIN_BOEHM_MAX_HEAP_SIZE) {
221 						fprintf (stderr, "max-heap-size must be at least %dMb.\n", MIN_BOEHM_MAX_HEAP_SIZE_IN_MB);
222 						exit (1);
223 					}
224 					GC_set_max_heap_size (max_heap);
225 				} else {
226 					fprintf (stderr, "max-heap-size must be an integer.\n");
227 					exit (1);
228 				}
229 				continue;
230 			} else if (g_str_has_prefix (opt, "toggleref-test")) {
231 				register_test_toggleref_callback ();
232 				continue;
233 			} else {
234 				/* Could be a parameter for sgen */
235 				/*
236 				fprintf (stderr, "MONO_GC_PARAMS must be a comma-delimited list of one or more of the following:\n");
237 				fprintf (stderr, "  max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
238 				exit (1);
239 				*/
240 			}
241 		}
242 		g_free (env);
243 		g_strfreev (opts);
244 	}
245 
246 	mono_thread_callbacks_init ();
247 	mono_thread_info_init (sizeof (MonoThreadInfo));
248 	mono_os_mutex_init (&mono_gc_lock);
249 	mono_os_mutex_init_recursive (&handle_section);
250 
251 	mono_thread_info_attach ();
252 
253 	GC_set_on_collection_event (on_gc_notification);
254 	GC_on_heap_resize = on_gc_heap_resize;
255 
256 	gc_initialized = TRUE;
257 }
258 
259 void
mono_gc_base_cleanup(void)260 mono_gc_base_cleanup (void)
261 {
262 	GC_finalizer_notifier = NULL;
263 }
264 
265 /**
266  * mono_gc_collect:
267  * \param generation GC generation identifier
268  *
269  * Perform a garbage collection for the given generation, higher numbers
270  * mean usually older objects. Collecting a high-numbered generation
271  * implies collecting also the lower-numbered generations.
272  * The maximum value for \p generation can be retrieved with a call to
273  * \c mono_gc_max_generation, so this function is usually called as:
274  *
275  * <code>mono_gc_collect (mono_gc_max_generation ());</code>
276  */
277 void
mono_gc_collect(int generation)278 mono_gc_collect (int generation)
279 {
280 #ifndef DISABLE_PERFCOUNTERS
281 	mono_atomic_inc_i32 (&mono_perfcounters->gc_induced);
282 #endif
283 	GC_gcollect ();
284 }
285 
286 /**
287  * mono_gc_max_generation:
288  *
289  * Get the maximum generation number used by the current garbage
290  * collector. The value will be 0 for the Boehm collector, 1 or more
291  * for the generational collectors.
292  *
293  * Returns: the maximum generation number.
294  */
295 int
mono_gc_max_generation(void)296 mono_gc_max_generation (void)
297 {
298 	return 0;
299 }
300 
301 /**
302  * mono_gc_get_generation:
303  * \param object a managed object
304  *
305  * Get the garbage collector's generation that \p object belongs to.
306  * Use this has a hint only.
307  *
308  * \returns a garbage collector generation number
309  */
310 int
mono_gc_get_generation(MonoObject * object)311 mono_gc_get_generation  (MonoObject *object)
312 {
313 	return 0;
314 }
315 
316 /**
317  * mono_gc_collection_count:
318  * \param generation a GC generation number
319  *
320  * Get how many times a garbage collection has been performed
321  * for the given \p generation number.
322  *
323  * \returns the number of garbage collections
324  */
325 int
mono_gc_collection_count(int generation)326 mono_gc_collection_count (int generation)
327 {
328 	return GC_gc_no;
329 }
330 
331 /**
332  * mono_gc_add_memory_pressure:
333  * \param value amount of bytes
334  *
335  * Adjust the garbage collector's view of how many bytes of memory
336  * are indirectly referenced by managed objects (for example unmanaged
337  * memory holding image or other binary data).
338  * This is a hint only to the garbage collector algorithm.
339  * Note that negative amounts of p value will decrease the memory
340  * pressure.
341  */
342 void
mono_gc_add_memory_pressure(gint64 value)343 mono_gc_add_memory_pressure (gint64 value)
344 {
345 }
346 
347 /**
348  * mono_gc_get_used_size:
349  *
350  * Get the approximate amount of memory used by managed objects.
351  *
352  * Returns: the amount of memory used in bytes
353  */
354 int64_t
mono_gc_get_used_size(void)355 mono_gc_get_used_size (void)
356 {
357 	return GC_get_heap_size () - GC_get_free_bytes ();
358 }
359 
360 /**
361  * mono_gc_get_heap_size:
362  *
363  * Get the amount of memory used by the garbage collector.
364  *
365  * Returns: the size of the heap in bytes
366  */
367 int64_t
mono_gc_get_heap_size(void)368 mono_gc_get_heap_size (void)
369 {
370 	return GC_get_heap_size ();
371 }
372 
373 gboolean
mono_gc_is_gc_thread(void)374 mono_gc_is_gc_thread (void)
375 {
376 	return GC_thread_is_registered ();
377 }
378 
379 gpointer
mono_gc_thread_attach(MonoThreadInfo * info)380 mono_gc_thread_attach (MonoThreadInfo* info)
381 {
382 	struct GC_stack_base sb;
383 	int res;
384 
385 	/* TODO: use GC_get_stack_base instead of baseptr. */
386 	sb.mem_base = info->stack_end;
387 	res = GC_register_my_thread (&sb);
388 	if (res == GC_UNIMPLEMENTED)
389 	    return NULL; /* Cannot happen with GC v7+. */
390 
391 	info->handle_stack = mono_handle_stack_alloc ();
392 
393 	return info;
394 }
395 
396 void
mono_gc_thread_detach_with_lock(MonoThreadInfo * p)397 mono_gc_thread_detach_with_lock (MonoThreadInfo *p)
398 {
399 	MonoNativeThreadId tid;
400 
401 	tid = mono_thread_info_get_tid (p);
402 
403 	if (p->runtime_thread)
404 		mono_threads_add_joinable_thread ((gpointer)tid);
405 
406 	mono_handle_stack_free (p->handle_stack);
407 	p->handle_stack = NULL;
408 }
409 
410 gboolean
mono_gc_thread_in_critical_region(MonoThreadInfo * info)411 mono_gc_thread_in_critical_region (MonoThreadInfo *info)
412 {
413 	return FALSE;
414 }
415 
416 gboolean
mono_object_is_alive(MonoObject * o)417 mono_object_is_alive (MonoObject* o)
418 {
419 	return GC_is_marked ((ptr_t)o);
420 }
421 
422 int
mono_gc_walk_heap(int flags,MonoGCReferences callback,void * data)423 mono_gc_walk_heap (int flags, MonoGCReferences callback, void *data)
424 {
425 	return 1;
426 }
427 
428 static gint64 gc_start_time;
429 
430 static void
on_gc_notification(GC_EventType event)431 on_gc_notification (GC_EventType event)
432 {
433 	MonoProfilerGCEvent e;
434 
435 	switch (event) {
436 	case GC_EVENT_PRE_STOP_WORLD:
437 		e = MONO_GC_EVENT_PRE_STOP_WORLD;
438 		MONO_GC_WORLD_STOP_BEGIN ();
439 		break;
440 
441 	case GC_EVENT_POST_STOP_WORLD:
442 		e = MONO_GC_EVENT_POST_STOP_WORLD;
443 		MONO_GC_WORLD_STOP_END ();
444 		break;
445 
446 	case GC_EVENT_PRE_START_WORLD:
447 		e = MONO_GC_EVENT_PRE_START_WORLD;
448 		MONO_GC_WORLD_RESTART_BEGIN (1);
449 		break;
450 
451 	case GC_EVENT_POST_START_WORLD:
452 		e = MONO_GC_EVENT_POST_START_WORLD;
453 		MONO_GC_WORLD_RESTART_END (1);
454 		break;
455 
456 	case GC_EVENT_START:
457 		e = MONO_GC_EVENT_START;
458 		MONO_GC_BEGIN (1);
459 #ifndef DISABLE_PERFCOUNTERS
460 		if (mono_perfcounters)
461 			mono_atomic_inc_i32 (&mono_perfcounters->gc_collections0);
462 #endif
463 		mono_atomic_inc_i32 (&gc_stats.major_gc_count);
464 		gc_start_time = mono_100ns_ticks ();
465 		break;
466 
467 	case GC_EVENT_END:
468 		e = MONO_GC_EVENT_END;
469 		MONO_GC_END (1);
470 #if defined(ENABLE_DTRACE) && defined(__sun__)
471 		/* This works around a dtrace -G problem on Solaris.
472 		   Limit its actual use to when the probe is enabled. */
473 		if (MONO_GC_END_ENABLED ())
474 			sleep(0);
475 #endif
476 
477 #ifndef DISABLE_PERFCOUNTERS
478 		if (mono_perfcounters) {
479 			guint64 heap_size = GC_get_heap_size ();
480 			guint64 used_size = heap_size - GC_get_free_bytes ();
481 			/* FIXME: change these to mono_atomic_store_i64 () */
482 			UnlockedWrite64 (&mono_perfcounters->gc_total_bytes, used_size);
483 			UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
484 			UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
485 			UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
486 		}
487 #endif
488 		UnlockedAdd64 (&gc_stats.major_gc_time, mono_100ns_ticks () - gc_start_time);
489 		mono_trace_message (MONO_TRACE_GC, "gc took %" G_GINT64_FORMAT " usecs", (mono_100ns_ticks () - gc_start_time) / 10);
490 		break;
491 	default:
492 		break;
493 	}
494 
495 	switch (event) {
496 	case GC_EVENT_MARK_START:
497 	case GC_EVENT_MARK_END:
498 	case GC_EVENT_RECLAIM_START:
499 	case GC_EVENT_RECLAIM_END:
500 		break;
501 	default:
502 		MONO_PROFILER_RAISE (gc_event, (e, 0));
503 		break;
504 	}
505 
506 	switch (event) {
507 	case GC_EVENT_PRE_STOP_WORLD:
508 		mono_thread_info_suspend_lock ();
509 		MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, 0));
510 		break;
511 	case GC_EVENT_POST_START_WORLD:
512 		mono_thread_info_suspend_unlock ();
513 		MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, 0));
514 		break;
515 	default:
516 		break;
517 	}
518 }
519 
520 
521 static void
on_gc_heap_resize(size_t new_size)522 on_gc_heap_resize (size_t new_size)
523 {
524 	guint64 heap_size = GC_get_heap_size ();
525 #ifndef DISABLE_PERFCOUNTERS
526 	if (mono_perfcounters) {
527 		/* FIXME: change these to mono_atomic_store_i64 () */
528 		UnlockedWrite64 (&mono_perfcounters->gc_committed_bytes, heap_size);
529 		UnlockedWrite64 (&mono_perfcounters->gc_reserved_bytes, heap_size);
530 		UnlockedWrite64 (&mono_perfcounters->gc_gen0size, heap_size);
531 	}
532 #endif
533 
534 	MONO_PROFILER_RAISE (gc_resize, (new_size));
535 }
536 
537 typedef struct {
538 	char *start;
539 	char *end;
540 } RootData;
541 
542 static gpointer
register_root(gpointer arg)543 register_root (gpointer arg)
544 {
545 	RootData* root_data = arg;
546 	g_hash_table_insert (roots, root_data->start, root_data->end);
547 	return NULL;
548 }
549 
550 int
mono_gc_register_root(char * start,size_t size,void * descr,MonoGCRootSource source,const char * msg)551 mono_gc_register_root (char *start, size_t size, void *descr, MonoGCRootSource source, const char *msg)
552 {
553 	RootData root_data;
554 	root_data.start = start;
555 	/* Boehm root processing requires one byte past end of region to be scanned */
556 	root_data.end = start + size + 1;
557 	GC_call_with_alloc_lock (register_root, &root_data);
558 
559 	return TRUE;
560 }
561 
562 int
mono_gc_register_root_wbarrier(char * start,size_t size,MonoGCDescriptor descr,MonoGCRootSource source,const char * msg)563 mono_gc_register_root_wbarrier (char *start, size_t size, MonoGCDescriptor descr, MonoGCRootSource source, const char *msg)
564 {
565 	return mono_gc_register_root (start, size, descr, source, msg);
566 }
567 
568 static gpointer
deregister_root(gpointer arg)569 deregister_root (gpointer arg)
570 {
571 	gboolean removed = g_hash_table_remove (roots, arg);
572 	g_assert (removed);
573 	return NULL;
574 }
575 
576 void
mono_gc_deregister_root(char * addr)577 mono_gc_deregister_root (char* addr)
578 {
579 	GC_call_with_alloc_lock (deregister_root, addr);
580 }
581 
582 static void
push_root(gpointer key,gpointer value,gpointer user_data)583 push_root (gpointer key, gpointer value, gpointer user_data)
584 {
585 	GC_push_all (key, value);
586 }
587 
588 static void
push_handle_stack(HandleStack * stack)589 push_handle_stack (HandleStack* stack)
590 {
591 	HandleChunk *cur = stack->bottom;
592 	HandleChunk *last = stack->top;
593 
594 	if (!cur)
595 		return;
596 
597 	while (cur) {
598 		if (cur->size > 0)
599 			GC_push_all (cur->elems, (char*)(cur->elems + cur->size) + 1);
600 		if (cur == last)
601 			break;
602 		cur = cur->next;
603 	}
604 }
605 
606 static void
mono_push_other_roots(void)607 mono_push_other_roots (void)
608 {
609 	g_hash_table_foreach (roots, push_root, NULL);
610 	FOREACH_THREAD (info) {
611 		HandleStack* stack = (HandleStack*)info->handle_stack;
612 		if (stack)
613 			push_handle_stack (stack);
614 	} FOREACH_THREAD_END
615 	if (default_push_other_roots)
616 		default_push_other_roots ();
617 }
618 
619 static void
mono_gc_weak_link_add(void ** link_addr,MonoObject * obj,gboolean track)620 mono_gc_weak_link_add (void **link_addr, MonoObject *obj, gboolean track)
621 {
622 	/* libgc requires that we use HIDE_POINTER... */
623 	*link_addr = (void*)HIDE_POINTER (obj);
624 	if (track)
625 		GC_REGISTER_LONG_LINK (link_addr, obj);
626 	else
627 		GC_GENERAL_REGISTER_DISAPPEARING_LINK (link_addr, obj);
628 }
629 
630 static void
mono_gc_weak_link_remove(void ** link_addr,gboolean track)631 mono_gc_weak_link_remove (void **link_addr, gboolean track)
632 {
633 	if (track)
634 		GC_unregister_long_link (link_addr);
635 	else
636 		GC_unregister_disappearing_link (link_addr);
637 	*link_addr = NULL;
638 }
639 
640 static gpointer
reveal_link(gpointer link_addr)641 reveal_link (gpointer link_addr)
642 {
643 	void **link_a = (void **)link_addr;
644 	return REVEAL_POINTER (*link_a);
645 }
646 
647 static MonoObject *
mono_gc_weak_link_get(void ** link_addr)648 mono_gc_weak_link_get (void **link_addr)
649 {
650 	MonoObject *obj = (MonoObject *)GC_call_with_alloc_lock (reveal_link, link_addr);
651 	if (obj == (MonoObject *) -1)
652 		return NULL;
653 	return obj;
654 }
655 
656 void*
mono_gc_make_descr_for_string(gsize * bitmap,int numbits)657 mono_gc_make_descr_for_string (gsize *bitmap, int numbits)
658 {
659 	return mono_gc_make_descr_from_bitmap (bitmap, numbits);
660 }
661 
662 void*
mono_gc_make_descr_for_object(gsize * bitmap,int numbits,size_t obj_size)663 mono_gc_make_descr_for_object (gsize *bitmap, int numbits, size_t obj_size)
664 {
665 	return mono_gc_make_descr_from_bitmap (bitmap, numbits);
666 }
667 
668 void*
mono_gc_make_descr_for_array(int vector,gsize * elem_bitmap,int numbits,size_t elem_size)669 mono_gc_make_descr_for_array (int vector, gsize *elem_bitmap, int numbits, size_t elem_size)
670 {
671 	/* libgc has no usable support for arrays... */
672 	return GC_NO_DESCRIPTOR;
673 }
674 
675 void*
mono_gc_make_descr_from_bitmap(gsize * bitmap,int numbits)676 mono_gc_make_descr_from_bitmap (gsize *bitmap, int numbits)
677 {
678 	/* It seems there are issues when the bitmap doesn't fit: play it safe */
679 	if (numbits >= 30)
680 		return GC_NO_DESCRIPTOR;
681 	else
682 		return (gpointer)GC_make_descriptor ((GC_bitmap)bitmap, numbits);
683 }
684 
685 void*
mono_gc_make_vector_descr(void)686 mono_gc_make_vector_descr (void)
687 {
688 	return NULL;
689 }
690 
691 void*
mono_gc_make_root_descr_all_refs(int numbits)692 mono_gc_make_root_descr_all_refs (int numbits)
693 {
694 	return NULL;
695 }
696 
697 void*
mono_gc_alloc_fixed(size_t size,void * descr,MonoGCRootSource source,const char * msg)698 mono_gc_alloc_fixed (size_t size, void *descr, MonoGCRootSource source, const char *msg)
699 {
700 	return GC_MALLOC_UNCOLLECTABLE (size);
701 }
702 
703 void
mono_gc_free_fixed(void * addr)704 mono_gc_free_fixed (void* addr)
705 {
706 	GC_FREE (addr);
707 }
708 
709 void *
mono_gc_alloc_obj(MonoVTable * vtable,size_t size)710 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
711 {
712 	MonoObject *obj;
713 
714 	if (!vtable->klass->has_references) {
715 		obj = (MonoObject *)GC_MALLOC_ATOMIC (size);
716 		if (G_UNLIKELY (!obj))
717 			return NULL;
718 
719 		obj->vtable = vtable;
720 		obj->synchronisation = NULL;
721 
722 		memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
723 	} else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
724 		obj = (MonoObject *)GC_GCJ_MALLOC (size, vtable);
725 		if (G_UNLIKELY (!obj))
726 			return NULL;
727 	} else {
728 		obj = (MonoObject *)GC_MALLOC (size);
729 		if (G_UNLIKELY (!obj))
730 			return NULL;
731 
732 		obj->vtable = vtable;
733 	}
734 
735 	if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
736 		MONO_PROFILER_RAISE (gc_allocation, (obj));
737 
738 	return obj;
739 }
740 
741 void *
mono_gc_alloc_vector(MonoVTable * vtable,size_t size,uintptr_t max_length)742 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
743 {
744 	MonoArray *obj;
745 
746 	if (!vtable->klass->has_references) {
747 		obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
748 		if (G_UNLIKELY (!obj))
749 			return NULL;
750 
751 		obj->obj.vtable = vtable;
752 		obj->obj.synchronisation = NULL;
753 
754 		memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
755 	} else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
756 		obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
757 		if (G_UNLIKELY (!obj))
758 			return NULL;
759 	} else {
760 		obj = (MonoArray *)GC_MALLOC (size);
761 		if (G_UNLIKELY (!obj))
762 			return NULL;
763 
764 		obj->obj.vtable = vtable;
765 	}
766 
767 	obj->max_length = max_length;
768 
769 	if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
770 		MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
771 
772 	return obj;
773 }
774 
775 void *
mono_gc_alloc_array(MonoVTable * vtable,size_t size,uintptr_t max_length,uintptr_t bounds_size)776 mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uintptr_t bounds_size)
777 {
778 	MonoArray *obj;
779 
780 	if (!vtable->klass->has_references) {
781 		obj = (MonoArray *)GC_MALLOC_ATOMIC (size);
782 		if (G_UNLIKELY (!obj))
783 			return NULL;
784 
785 		obj->obj.vtable = vtable;
786 		obj->obj.synchronisation = NULL;
787 
788 		memset ((char *) obj + sizeof (MonoObject), 0, size - sizeof (MonoObject));
789 	} else if (vtable->gc_descr != GC_NO_DESCRIPTOR) {
790 		obj = (MonoArray *)GC_GCJ_MALLOC (size, vtable);
791 		if (G_UNLIKELY (!obj))
792 			return NULL;
793 	} else {
794 		obj = (MonoArray *)GC_MALLOC (size);
795 		if (G_UNLIKELY (!obj))
796 			return NULL;
797 
798 		obj->obj.vtable = vtable;
799 	}
800 
801 	obj->max_length = max_length;
802 
803 	if (bounds_size)
804 		obj->bounds = (MonoArrayBounds *) ((char *) obj + size - bounds_size);
805 
806 	if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
807 		MONO_PROFILER_RAISE (gc_allocation, (&obj->obj));
808 
809 	return obj;
810 }
811 
812 void *
mono_gc_alloc_string(MonoVTable * vtable,size_t size,gint32 len)813 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
814 {
815 	MonoString *obj = (MonoString *)GC_MALLOC_ATOMIC (size);
816 	if (G_UNLIKELY (!obj))
817 		return NULL;
818 
819 	obj->object.vtable = vtable;
820 	obj->object.synchronisation = NULL;
821 	obj->length = len;
822 	obj->chars [len] = 0;
823 
824 	if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
825 		MONO_PROFILER_RAISE (gc_allocation, (&obj->object));
826 
827 	return obj;
828 }
829 
830 void*
mono_gc_alloc_mature(MonoVTable * vtable,size_t size)831 mono_gc_alloc_mature (MonoVTable *vtable, size_t size)
832 {
833 	return mono_gc_alloc_obj (vtable, size);
834 }
835 
836 void*
mono_gc_alloc_pinned_obj(MonoVTable * vtable,size_t size)837 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
838 {
839 	return mono_gc_alloc_obj (vtable, size);
840 }
841 
842 int
mono_gc_invoke_finalizers(void)843 mono_gc_invoke_finalizers (void)
844 {
845 	/* There is a bug in GC_invoke_finalizer () in versions <= 6.2alpha4:
846 	 * the 'mem_freed' variable is not initialized when there are no
847 	 * objects to finalize, which leads to strange behavior later on.
848 	 * The check is necessary to work around that bug.
849 	 */
850 	if (GC_should_invoke_finalizers ())
851 		return GC_invoke_finalizers ();
852 	return 0;
853 }
854 
855 MonoBoolean
mono_gc_pending_finalizers(void)856 mono_gc_pending_finalizers (void)
857 {
858 	return GC_should_invoke_finalizers ();
859 }
860 
861 void
mono_gc_wbarrier_set_field(MonoObject * obj,gpointer field_ptr,MonoObject * value)862 mono_gc_wbarrier_set_field (MonoObject *obj, gpointer field_ptr, MonoObject* value)
863 {
864 	*(void**)field_ptr = value;
865 }
866 
867 void
mono_gc_wbarrier_set_arrayref(MonoArray * arr,gpointer slot_ptr,MonoObject * value)868 mono_gc_wbarrier_set_arrayref (MonoArray *arr, gpointer slot_ptr, MonoObject* value)
869 {
870 	*(void**)slot_ptr = value;
871 }
872 
873 void
mono_gc_wbarrier_arrayref_copy(gpointer dest_ptr,gpointer src_ptr,int count)874 mono_gc_wbarrier_arrayref_copy (gpointer dest_ptr, gpointer src_ptr, int count)
875 {
876 	mono_gc_memmove_aligned (dest_ptr, src_ptr, count * sizeof (gpointer));
877 }
878 
879 void
mono_gc_wbarrier_generic_store(gpointer ptr,MonoObject * value)880 mono_gc_wbarrier_generic_store (gpointer ptr, MonoObject* value)
881 {
882 	*(void**)ptr = value;
883 }
884 
885 void
mono_gc_wbarrier_generic_store_atomic(gpointer ptr,MonoObject * value)886 mono_gc_wbarrier_generic_store_atomic (gpointer ptr, MonoObject *value)
887 {
888 	mono_atomic_store_ptr ((volatile gpointer *)ptr, value);
889 }
890 
891 void
mono_gc_wbarrier_generic_nostore(gpointer ptr)892 mono_gc_wbarrier_generic_nostore (gpointer ptr)
893 {
894 }
895 
896 void
mono_gc_wbarrier_value_copy(gpointer dest,gpointer src,int count,MonoClass * klass)897 mono_gc_wbarrier_value_copy (gpointer dest, gpointer src, int count, MonoClass *klass)
898 {
899 	mono_gc_memmove_atomic (dest, src, count * mono_class_value_size (klass, NULL));
900 }
901 
902 void
mono_gc_wbarrier_object_copy(MonoObject * obj,MonoObject * src)903 mono_gc_wbarrier_object_copy (MonoObject* obj, MonoObject *src)
904 {
905 	/* do not copy the sync state */
906 	mono_gc_memmove_aligned ((char*)obj + sizeof (MonoObject), (char*)src + sizeof (MonoObject),
907 			mono_object_class (obj)->instance_size - sizeof (MonoObject));
908 }
909 
910 void
mono_gc_clear_domain(MonoDomain * domain)911 mono_gc_clear_domain (MonoDomain *domain)
912 {
913 }
914 
915 void
mono_gc_suspend_finalizers(void)916 mono_gc_suspend_finalizers (void)
917 {
918 }
919 
920 int
mono_gc_get_suspend_signal(void)921 mono_gc_get_suspend_signal (void)
922 {
923 	return GC_get_suspend_signal ();
924 }
925 
926 int
mono_gc_get_restart_signal(void)927 mono_gc_get_restart_signal (void)
928 {
929 	return GC_get_thr_restart_signal ();
930 }
931 
932 #if defined(USE_COMPILER_TLS) && defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
933 extern __thread void* GC_thread_tls;
934 #include "metadata-internals.h"
935 
936 static int
shift_amount(int v)937 shift_amount (int v)
938 {
939 	int i = 0;
940 	while (!(v & (1 << i)))
941 		i++;
942 	return i;
943 }
944 
945 enum {
946 	ATYPE_FREEPTR,
947 	ATYPE_FREEPTR_FOR_BOX,
948 	ATYPE_NORMAL,
949 	ATYPE_GCJ,
950 	ATYPE_STRING,
951 	ATYPE_NUM
952 };
953 
954 static MonoMethod*
create_allocator(int atype,int tls_key,gboolean slowpath)955 create_allocator (int atype, int tls_key, gboolean slowpath)
956 {
957 	int index_var, bytes_var, my_fl_var, my_entry_var;
958 	guint32 no_freelist_branch, not_small_enough_branch = 0;
959 	guint32 size_overflow_branch = 0;
960 	MonoMethodBuilder *mb;
961 	MonoMethod *res;
962 	MonoMethodSignature *csig;
963 	const char *name = NULL;
964 	WrapperInfo *info;
965 
966 	g_assert_not_reached ();
967 
968 	if (atype == ATYPE_FREEPTR) {
969 		name = slowpath ? "SlowAllocPtrfree" : "AllocPtrfree";
970 	} else if (atype == ATYPE_FREEPTR_FOR_BOX) {
971 		name = slowpath ? "SlowAllocPtrfreeBox" : "AllocPtrfreeBox";
972 	} else if (atype == ATYPE_NORMAL) {
973 		name = slowpath ? "SlowAlloc" : "Alloc";
974 	} else if (atype == ATYPE_GCJ) {
975 		name = slowpath ? "SlowAllocGcj" : "AllocGcj";
976 	} else if (atype == ATYPE_STRING) {
977 		name = slowpath ? "SlowAllocString" : "AllocString";
978 	} else {
979 		g_assert_not_reached ();
980 	}
981 
982 	csig = mono_metadata_signature_alloc (mono_defaults.corlib, 2);
983 
984 	if (atype == ATYPE_STRING) {
985 		csig->ret = &mono_defaults.string_class->byval_arg;
986 		csig->params [0] = &mono_defaults.int_class->byval_arg;
987 		csig->params [1] = &mono_defaults.int32_class->byval_arg;
988 	} else {
989 		csig->ret = &mono_defaults.object_class->byval_arg;
990 		csig->params [0] = &mono_defaults.int_class->byval_arg;
991 		csig->params [1] = &mono_defaults.int32_class->byval_arg;
992 	}
993 
994 	mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
995 
996 	if (slowpath)
997 		goto always_slowpath;
998 
999 	bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
1000 	if (atype == ATYPE_STRING) {
1001 		/* a string alloator method takes the args: (vtable, len) */
1002 		/* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
1003 		mono_mb_emit_ldarg (mb, 1);
1004 		mono_mb_emit_icon (mb, 1);
1005 		mono_mb_emit_byte (mb, MONO_CEE_ADD);
1006 		mono_mb_emit_icon (mb, 1);
1007 		mono_mb_emit_byte (mb, MONO_CEE_SHL);
1008 		// sizeof (MonoString) might include padding
1009 		mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, chars));
1010 		mono_mb_emit_byte (mb, MONO_CEE_ADD);
1011 		mono_mb_emit_stloc (mb, bytes_var);
1012 	} else {
1013 		mono_mb_emit_ldarg (mb, 1);
1014 		mono_mb_emit_stloc (mb, bytes_var);
1015 	}
1016 
1017 	/* this is needed for strings/arrays only as the other big types are never allocated with this method */
1018 	if (atype == ATYPE_STRING) {
1019 		/* check for size */
1020 		/* if (!SMALL_ENOUGH (bytes)) jump slow_path;*/
1021 		mono_mb_emit_ldloc (mb, bytes_var);
1022 		mono_mb_emit_icon (mb, (NFREELISTS-1) * GRANULARITY);
1023 		not_small_enough_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
1024 		/* check for overflow */
1025 		mono_mb_emit_ldloc (mb, bytes_var);
1026 		mono_mb_emit_icon (mb, sizeof (MonoString));
1027 		size_overflow_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
1028 	}
1029 
1030 	/* int index = INDEX_FROM_BYTES(bytes); */
1031 	index_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
1032 
1033 	mono_mb_emit_ldloc (mb, bytes_var);
1034 	mono_mb_emit_icon (mb, GRANULARITY - 1);
1035 	mono_mb_emit_byte (mb, MONO_CEE_ADD);
1036 	mono_mb_emit_icon (mb, shift_amount (GRANULARITY));
1037 	mono_mb_emit_byte (mb, MONO_CEE_SHR_UN);
1038 	mono_mb_emit_icon (mb, shift_amount (sizeof (gpointer)));
1039 	mono_mb_emit_byte (mb, MONO_CEE_SHL);
1040 	/* index var is already adjusted into bytes */
1041 	mono_mb_emit_stloc (mb, index_var);
1042 
1043 	my_fl_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1044 	my_entry_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1045 	/* my_fl = ((GC_thread)tsd) -> ptrfree_freelists + index; */
1046 	mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
1047 	mono_mb_emit_byte (mb, 0x0D); /* CEE_MONO_TLS */
1048 	mono_mb_emit_i4 (mb, tls_key);
1049 	if (atype == ATYPE_FREEPTR || atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING)
1050 		mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1051 					+ G_STRUCT_OFFSET (struct thread_local_freelists,
1052 							   ptrfree_freelists));
1053 	else if (atype == ATYPE_NORMAL)
1054 		mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1055 					+ G_STRUCT_OFFSET (struct thread_local_freelists,
1056 							   normal_freelists));
1057 	else if (atype == ATYPE_GCJ)
1058 		mono_mb_emit_icon (mb, G_STRUCT_OFFSET (struct GC_Thread_Rep, tlfs)
1059 					+ G_STRUCT_OFFSET (struct thread_local_freelists,
1060 							   gcj_freelists));
1061 	else
1062 		g_assert_not_reached ();
1063 	mono_mb_emit_byte (mb, MONO_CEE_ADD);
1064 	mono_mb_emit_ldloc (mb, index_var);
1065 	mono_mb_emit_byte (mb, MONO_CEE_ADD);
1066 	mono_mb_emit_stloc (mb, my_fl_var);
1067 
1068 	/* my_entry = *my_fl; */
1069 	mono_mb_emit_ldloc (mb, my_fl_var);
1070 	mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1071 	mono_mb_emit_stloc (mb, my_entry_var);
1072 
1073 	/* if (EXPECT((word)my_entry >= HBLKSIZE, 1)) { */
1074 	mono_mb_emit_ldloc (mb, my_entry_var);
1075 	mono_mb_emit_icon (mb, HBLKSIZE);
1076 	no_freelist_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
1077 
1078 	/* ptr_t next = obj_link(my_entry); *my_fl = next; */
1079 	mono_mb_emit_ldloc (mb, my_fl_var);
1080 	mono_mb_emit_ldloc (mb, my_entry_var);
1081 	mono_mb_emit_byte (mb, MONO_CEE_LDIND_I);
1082 	mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1083 
1084 	/* set the vtable and clear the words in the object */
1085 	mono_mb_emit_ldloc (mb, my_entry_var);
1086 	mono_mb_emit_ldarg (mb, 0);
1087 	mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1088 
1089 	if (atype == ATYPE_FREEPTR) {
1090 		int start_var, end_var, start_loop;
1091 		/* end = my_entry + bytes; start = my_entry + sizeof (gpointer);
1092 		 */
1093 		start_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1094 		end_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
1095 		mono_mb_emit_ldloc (mb, my_entry_var);
1096 		mono_mb_emit_ldloc (mb, bytes_var);
1097 		mono_mb_emit_byte (mb, MONO_CEE_ADD);
1098 		mono_mb_emit_stloc (mb, end_var);
1099 		mono_mb_emit_ldloc (mb, my_entry_var);
1100 		mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1101 		mono_mb_emit_byte (mb, MONO_CEE_ADD);
1102 		mono_mb_emit_stloc (mb, start_var);
1103 		/*
1104 		 * do {
1105 		 * 	*start++ = NULL;
1106 		 * } while (start < end);
1107 		 */
1108 		start_loop = mono_mb_get_label (mb);
1109 		mono_mb_emit_ldloc (mb, start_var);
1110 		mono_mb_emit_icon (mb, 0);
1111 		mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1112 		mono_mb_emit_ldloc (mb, start_var);
1113 		mono_mb_emit_icon (mb, sizeof (gpointer));
1114 		mono_mb_emit_byte (mb, MONO_CEE_ADD);
1115 		mono_mb_emit_stloc (mb, start_var);
1116 
1117 		mono_mb_emit_ldloc (mb, start_var);
1118 		mono_mb_emit_ldloc (mb, end_var);
1119 		mono_mb_emit_byte (mb, MONO_CEE_BLT_UN_S);
1120 		mono_mb_emit_byte (mb, start_loop - (mono_mb_get_label (mb) + 1));
1121 	} else if (atype == ATYPE_FREEPTR_FOR_BOX || atype == ATYPE_STRING) {
1122 		/* need to clear just the sync pointer */
1123 		mono_mb_emit_ldloc (mb, my_entry_var);
1124 		mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
1125 		mono_mb_emit_byte (mb, MONO_CEE_ADD);
1126 		mono_mb_emit_icon (mb, 0);
1127 		mono_mb_emit_byte (mb, MONO_CEE_STIND_I);
1128 	}
1129 
1130 	if (atype == ATYPE_STRING) {
1131 		/* need to set length and clear the last char */
1132 		/* s->length = len; */
1133 		mono_mb_emit_ldloc (mb, my_entry_var);
1134 		mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
1135 		mono_mb_emit_byte (mb, MONO_CEE_ADD);
1136 		mono_mb_emit_ldarg (mb, 1);
1137 		mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
1138 		/* s->chars [len] = 0; */
1139 		mono_mb_emit_ldloc (mb, my_entry_var);
1140 		mono_mb_emit_ldloc (mb, bytes_var);
1141 		mono_mb_emit_icon (mb, 2);
1142 		mono_mb_emit_byte (mb, MONO_CEE_SUB);
1143 		mono_mb_emit_byte (mb, MONO_CEE_ADD);
1144 		mono_mb_emit_icon (mb, 0);
1145 		mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
1146 	}
1147 
1148 	/* return my_entry; */
1149 	mono_mb_emit_ldloc (mb, my_entry_var);
1150 	mono_mb_emit_byte (mb, MONO_CEE_RET);
1151 
1152 	mono_mb_patch_short_branch (mb, no_freelist_branch);
1153 	if (not_small_enough_branch > 0)
1154 		mono_mb_patch_short_branch (mb, not_small_enough_branch);
1155 	if (size_overflow_branch > 0)
1156 		mono_mb_patch_short_branch (mb, size_overflow_branch);
1157 
1158 	/* the slow path: we just call back into the runtime */
1159  always_slowpath:
1160 	if (atype == ATYPE_STRING) {
1161 		mono_mb_emit_ldarg (mb, 1);
1162 		mono_mb_emit_icall (mb, ves_icall_string_alloc);
1163 	} else {
1164 		mono_mb_emit_ldarg (mb, 0);
1165 		mono_mb_emit_icall (mb, ves_icall_object_new_specific);
1166 	}
1167 
1168 	mono_mb_emit_byte (mb, MONO_CEE_RET);
1169 
1170 	info = mono_wrapper_info_create (mb, WRAPPER_SUBTYPE_NONE);
1171 	info->d.alloc.gc_name = "boehm";
1172 	info->d.alloc.alloc_type = atype;
1173 	mb->init_locals = FALSE;
1174 
1175 	res = mono_mb_create (mb, csig, 8, info);
1176 	mono_mb_free (mb);
1177 
1178 	return res;
1179 }
1180 
1181 static MonoMethod* alloc_method_cache [ATYPE_NUM];
1182 static MonoMethod* slowpath_alloc_method_cache [ATYPE_NUM];
1183 
1184 gboolean
mono_gc_is_critical_method(MonoMethod * method)1185 mono_gc_is_critical_method (MonoMethod *method)
1186 {
1187 	int i;
1188 
1189 	for (i = 0; i < ATYPE_NUM; ++i)
1190 		if (method == alloc_method_cache [i] || method == slowpath_alloc_method_cache [i])
1191 			return TRUE;
1192 
1193 	return FALSE;
1194 }
1195 
1196 /*
1197  * If possible, generate a managed method that can quickly allocate objects in class
1198  * @klass. The method will typically have an thread-local inline allocation sequence.
1199  * The signature of the called method is:
1200  * 	object allocate (MonoVTable *vtable)
1201  * The thread local alloc logic is taken from libgc/pthread_support.c.
1202  */
1203 MonoMethod*
mono_gc_get_managed_allocator(MonoClass * klass,gboolean for_box,gboolean known_instance_size)1204 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1205 {
1206 	int atype;
1207 
1208 	/*
1209 	 * Tls implementation changed, we jump to tls native getters/setters.
1210 	 * Is boehm managed allocator ok with this ? Do we even care ?
1211 	 */
1212 	return NULL;
1213 
1214 	if (!SMALL_ENOUGH (klass->instance_size))
1215 		return NULL;
1216 	if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
1217 		return NULL;
1218 	if (G_UNLIKELY (mono_profiler_allocations_enabled ()))
1219 		return NULL;
1220 	if (klass->rank)
1221 		return NULL;
1222 	if (mono_class_is_open_constructed_type (&klass->byval_arg))
1223 		return NULL;
1224 	if (klass->byval_arg.type == MONO_TYPE_STRING) {
1225 		atype = ATYPE_STRING;
1226 	} else if (!known_instance_size) {
1227 		return NULL;
1228 	} else if (!klass->has_references) {
1229 		if (for_box)
1230 			atype = ATYPE_FREEPTR_FOR_BOX;
1231 		else
1232 			atype = ATYPE_FREEPTR;
1233 	} else {
1234 		return NULL;
1235 		/*
1236 		 * disabled because we currently do a runtime choice anyway, to
1237 		 * deal with multiple appdomains.
1238 		if (vtable->gc_descr != GC_NO_DESCRIPTOR)
1239 			atype = ATYPE_GCJ;
1240 		else
1241 			atype = ATYPE_NORMAL;
1242 		*/
1243 	}
1244 	return mono_gc_get_managed_allocator_by_type (atype, MANAGED_ALLOCATOR_REGULAR);
1245 }
1246 
1247 MonoMethod*
mono_gc_get_managed_array_allocator(MonoClass * klass)1248 mono_gc_get_managed_array_allocator (MonoClass *klass)
1249 {
1250 	return NULL;
1251 }
1252 
1253 /**
1254  * mono_gc_get_managed_allocator_by_type:
1255  *
1256  *   Return a managed allocator method corresponding to allocator type ATYPE.
1257  */
1258 MonoMethod*
mono_gc_get_managed_allocator_by_type(int atype,ManagedAllocatorVariant variant)1259 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1260 {
1261 	MonoMethod *res;
1262 	gboolean slowpath = variant != MANAGED_ALLOCATOR_REGULAR;
1263 	MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
1264 
1265 	return NULL;
1266 
1267 	res = cache [atype];
1268 	if (res)
1269 		return res;
1270 
1271 	res = create_allocator (atype, -1, slowpath);
1272 	mono_os_mutex_lock (&mono_gc_lock);
1273 	if (cache [atype]) {
1274 		mono_free_method (res);
1275 		res = cache [atype];
1276 	} else {
1277 		mono_memory_barrier ();
1278 		cache [atype] = res;
1279 	}
1280 	mono_os_mutex_unlock (&mono_gc_lock);
1281 	return res;
1282 }
1283 
1284 guint32
mono_gc_get_managed_allocator_types(void)1285 mono_gc_get_managed_allocator_types (void)
1286 {
1287 	return ATYPE_NUM;
1288 }
1289 
1290 MonoMethod*
mono_gc_get_write_barrier(void)1291 mono_gc_get_write_barrier (void)
1292 {
1293 	g_assert_not_reached ();
1294 	return NULL;
1295 }
1296 
1297 #else
1298 
1299 gboolean
mono_gc_is_critical_method(MonoMethod * method)1300 mono_gc_is_critical_method (MonoMethod *method)
1301 {
1302 	return FALSE;
1303 }
1304 
1305 MonoMethod*
mono_gc_get_managed_allocator(MonoClass * klass,gboolean for_box,gboolean known_instance_size)1306 mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean known_instance_size)
1307 {
1308 	return NULL;
1309 }
1310 
1311 MonoMethod*
mono_gc_get_managed_array_allocator(MonoClass * klass)1312 mono_gc_get_managed_array_allocator (MonoClass *klass)
1313 {
1314 	return NULL;
1315 }
1316 
1317 MonoMethod*
mono_gc_get_managed_allocator_by_type(int atype,ManagedAllocatorVariant variant)1318 mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
1319 {
1320 	return NULL;
1321 }
1322 
1323 guint32
mono_gc_get_managed_allocator_types(void)1324 mono_gc_get_managed_allocator_types (void)
1325 {
1326 	return 0;
1327 }
1328 
1329 MonoMethod*
mono_gc_get_write_barrier(void)1330 mono_gc_get_write_barrier (void)
1331 {
1332 	g_assert_not_reached ();
1333 	return NULL;
1334 }
1335 
1336 #endif
1337 
1338 MonoMethod*
mono_gc_get_specific_write_barrier(gboolean is_concurrent)1339 mono_gc_get_specific_write_barrier (gboolean is_concurrent)
1340 {
1341 	g_assert_not_reached ();
1342 	return NULL;
1343 }
1344 
1345 int
mono_gc_get_aligned_size_for_allocator(int size)1346 mono_gc_get_aligned_size_for_allocator (int size)
1347 {
1348 	return size;
1349 }
1350 
1351 const char *
mono_gc_get_gc_name(void)1352 mono_gc_get_gc_name (void)
1353 {
1354 	return "boehm";
1355 }
1356 
1357 void*
mono_gc_invoke_with_gc_lock(MonoGCLockedCallbackFunc func,void * data)1358 mono_gc_invoke_with_gc_lock (MonoGCLockedCallbackFunc func, void *data)
1359 {
1360 	return GC_call_with_alloc_lock (func, data);
1361 }
1362 
1363 char*
mono_gc_get_description(void)1364 mono_gc_get_description (void)
1365 {
1366 	return g_strdup (DEFAULT_GC_NAME);
1367 }
1368 
1369 void
mono_gc_set_desktop_mode(void)1370 mono_gc_set_desktop_mode (void)
1371 {
1372 	GC_dont_expand = 1;
1373 }
1374 
1375 gboolean
mono_gc_is_moving(void)1376 mono_gc_is_moving (void)
1377 {
1378 	return FALSE;
1379 }
1380 
1381 gboolean
mono_gc_is_disabled(void)1382 mono_gc_is_disabled (void)
1383 {
1384 	if (GC_dont_gc || g_hasenv ("GC_DONT_GC"))
1385 		return TRUE;
1386 	else
1387 		return FALSE;
1388 }
1389 
1390 void
mono_gc_wbarrier_range_copy(gpointer _dest,gpointer _src,int size)1391 mono_gc_wbarrier_range_copy (gpointer _dest, gpointer _src, int size)
1392 {
1393 	g_assert_not_reached ();
1394 }
1395 
1396 void*
mono_gc_get_range_copy_func(void)1397 mono_gc_get_range_copy_func (void)
1398 {
1399 	return &mono_gc_wbarrier_range_copy;
1400 }
1401 
1402 guint8*
mono_gc_get_card_table(int * shift_bits,gpointer * card_mask)1403 mono_gc_get_card_table (int *shift_bits, gpointer *card_mask)
1404 {
1405 	g_assert_not_reached ();
1406 	return NULL;
1407 }
1408 
1409 gboolean
mono_gc_card_table_nursery_check(void)1410 mono_gc_card_table_nursery_check (void)
1411 {
1412 	g_assert_not_reached ();
1413 	return TRUE;
1414 }
1415 
1416 void*
mono_gc_get_nursery(int * shift_bits,size_t * size)1417 mono_gc_get_nursery (int *shift_bits, size_t *size)
1418 {
1419 	return NULL;
1420 }
1421 
1422 gboolean
mono_gc_precise_stack_mark_enabled(void)1423 mono_gc_precise_stack_mark_enabled (void)
1424 {
1425 	return FALSE;
1426 }
1427 
1428 FILE *
mono_gc_get_logfile(void)1429 mono_gc_get_logfile (void)
1430 {
1431 	return NULL;
1432 }
1433 
1434 void
mono_gc_params_set(const char * options)1435 mono_gc_params_set (const char* options)
1436 {
1437 }
1438 
1439 void
mono_gc_debug_set(const char * options)1440 mono_gc_debug_set (const char* options)
1441 {
1442 }
1443 
1444 void
mono_gc_conservatively_scan_area(void * start,void * end)1445 mono_gc_conservatively_scan_area (void *start, void *end)
1446 {
1447 	g_assert_not_reached ();
1448 }
1449 
1450 void *
mono_gc_scan_object(void * obj,void * gc_data)1451 mono_gc_scan_object (void *obj, void *gc_data)
1452 {
1453 	g_assert_not_reached ();
1454 	return NULL;
1455 }
1456 
1457 gsize*
mono_gc_get_bitmap_for_descr(void * descr,int * numbits)1458 mono_gc_get_bitmap_for_descr (void *descr, int *numbits)
1459 {
1460 	g_assert_not_reached ();
1461 	return NULL;
1462 }
1463 
1464 void
mono_gc_set_gc_callbacks(MonoGCCallbacks * callbacks)1465 mono_gc_set_gc_callbacks (MonoGCCallbacks *callbacks)
1466 {
1467 }
1468 
1469 void
mono_gc_set_stack_end(void * stack_end)1470 mono_gc_set_stack_end (void *stack_end)
1471 {
1472 }
1473 
mono_gc_set_skip_thread(gboolean value)1474 void mono_gc_set_skip_thread (gboolean value)
1475 {
1476 }
1477 
1478 void
mono_gc_register_for_finalization(MonoObject * obj,void * user_data)1479 mono_gc_register_for_finalization (MonoObject *obj, void *user_data)
1480 {
1481 	guint offset = 0;
1482 
1483 #ifndef GC_DEBUG
1484 	/* This assertion is not valid when GC_DEBUG is defined */
1485 	g_assert (GC_base (obj) == (char*)obj - offset);
1486 #endif
1487 
1488 	GC_REGISTER_FINALIZER_NO_ORDER ((char*)obj - offset, (GC_finalization_proc)user_data, GUINT_TO_POINTER (offset), NULL, NULL);
1489 }
1490 
1491 #ifndef HOST_WIN32
1492 int
mono_gc_pthread_create(pthread_t * new_thread,const pthread_attr_t * attr,void * (* start_routine)(void *),void * arg)1493 mono_gc_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
1494 {
1495 	/* it is being replaced by GC_pthread_create on some
1496 	 * platforms, see libgc/include/gc_pthread_redirects.h */
1497 	return pthread_create (new_thread, attr, start_routine, arg);
1498 }
1499 #endif
1500 
1501 #ifdef HOST_WIN32
mono_gc_dllmain(HMODULE module_handle,DWORD reason,LPVOID reserved)1502 BOOL APIENTRY mono_gc_dllmain (HMODULE module_handle, DWORD reason, LPVOID reserved)
1503 {
1504 	return GC_DllMain (module_handle, reason, reserved);
1505 }
1506 #endif
1507 
1508 guint
mono_gc_get_vtable_bits(MonoClass * klass)1509 mono_gc_get_vtable_bits (MonoClass *klass)
1510 {
1511 	if (fin_callbacks.is_class_finalization_aware) {
1512 		if (fin_callbacks.is_class_finalization_aware (klass))
1513 			return BOEHM_GC_BIT_FINALIZER_AWARE;
1514 	}
1515 	return 0;
1516 }
1517 
1518 /*
1519  * mono_gc_register_altstack:
1520  *
1521  *   Register the dimensions of the normal stack and altstack with the collector.
1522  * Currently, STACK/STACK_SIZE is only used when the thread is suspended while it is on an altstack.
1523  */
1524 void
mono_gc_register_altstack(gpointer stack,gint32 stack_size,gpointer altstack,gint32 altstack_size)1525 mono_gc_register_altstack (gpointer stack, gint32 stack_size, gpointer altstack, gint32 altstack_size)
1526 {
1527 	GC_register_altstack (stack, stack_size, altstack, altstack_size);
1528 }
1529 
1530 int
mono_gc_get_los_limit(void)1531 mono_gc_get_los_limit (void)
1532 {
1533 	return G_MAXINT;
1534 }
1535 
1536 void
mono_gc_set_string_length(MonoString * str,gint32 new_length)1537 mono_gc_set_string_length (MonoString *str, gint32 new_length)
1538 {
1539 	mono_unichar2 *new_end = str->chars + new_length;
1540 
1541 	/* zero the discarded string. This null-delimits the string and allows
1542 	 * the space to be reclaimed by SGen. */
1543 
1544 	memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
1545 	str->length = new_length;
1546 }
1547 
1548 gboolean
mono_gc_user_markers_supported(void)1549 mono_gc_user_markers_supported (void)
1550 {
1551 	return FALSE;
1552 }
1553 
1554 void *
mono_gc_make_root_descr_user(MonoGCRootMarkFunc marker)1555 mono_gc_make_root_descr_user (MonoGCRootMarkFunc marker)
1556 {
1557 	g_assert_not_reached ();
1558 	return NULL;
1559 }
1560 
1561 /* Toggleref support */
1562 
1563 void
mono_gc_toggleref_add(MonoObject * object,mono_bool strong_ref)1564 mono_gc_toggleref_add (MonoObject *object, mono_bool strong_ref)
1565 {
1566 	if (GC_toggleref_add ((GC_PTR)object, (int)strong_ref) != GC_SUCCESS)
1567 	    g_error ("GC_toggleref_add failed\n");
1568 }
1569 
1570 void
mono_gc_toggleref_register_callback(MonoToggleRefStatus (* proccess_toggleref)(MonoObject * obj))1571 mono_gc_toggleref_register_callback (MonoToggleRefStatus (*proccess_toggleref) (MonoObject *obj))
1572 {
1573 	GC_set_toggleref_func ((GC_ToggleRefStatus (*) (GC_PTR obj)) proccess_toggleref);
1574 }
1575 
1576 /* Test support code */
1577 
1578 static MonoToggleRefStatus
test_toggleref_callback(MonoObject * obj)1579 test_toggleref_callback (MonoObject *obj)
1580 {
1581 	static MonoClassField *mono_toggleref_test_field;
1582 	MonoToggleRefStatus status = MONO_TOGGLE_REF_DROP;
1583 
1584 	if (!mono_toggleref_test_field) {
1585 		mono_toggleref_test_field = mono_class_get_field_from_name (mono_object_get_class (obj), "__test");
1586 		g_assert (mono_toggleref_test_field);
1587 	}
1588 
1589 	mono_field_get_value (obj, mono_toggleref_test_field, &status);
1590 	printf ("toggleref-cb obj %d\n", status);
1591 	return status;
1592 }
1593 
1594 static void
register_test_toggleref_callback(void)1595 register_test_toggleref_callback (void)
1596 {
1597 	mono_gc_toggleref_register_callback (test_toggleref_callback);
1598 }
1599 
1600 static gboolean
is_finalization_aware(MonoObject * obj)1601 is_finalization_aware (MonoObject *obj)
1602 {
1603 	MonoVTable *vt = obj->vtable;
1604 	return (vt->gc_bits & BOEHM_GC_BIT_FINALIZER_AWARE) == BOEHM_GC_BIT_FINALIZER_AWARE;
1605 }
1606 
1607 static void
fin_notifier(MonoObject * obj)1608 fin_notifier (MonoObject *obj)
1609 {
1610 	if (is_finalization_aware (obj))
1611 		fin_callbacks.object_queued_for_finalization (obj);
1612 }
1613 
1614 void
mono_gc_register_finalizer_callbacks(MonoGCFinalizerCallbacks * callbacks)1615 mono_gc_register_finalizer_callbacks (MonoGCFinalizerCallbacks *callbacks)
1616 {
1617 	if (callbacks->version != MONO_GC_FINALIZER_EXTENSION_VERSION)
1618 		g_error ("Invalid finalizer callback version. Expected %d but got %d\n", MONO_GC_FINALIZER_EXTENSION_VERSION, callbacks->version);
1619 
1620 	fin_callbacks = *callbacks;
1621 
1622 	GC_set_await_finalize_proc ((void (*) (GC_PTR))fin_notifier);
1623 }
1624 
1625 #define BITMAP_SIZE (sizeof (*((HandleData *)NULL)->bitmap) * CHAR_BIT)
1626 
1627 static inline gboolean
slot_occupied(HandleData * handles,guint slot)1628 slot_occupied (HandleData *handles, guint slot) {
1629 	return handles->bitmap [slot / BITMAP_SIZE] & (1 << (slot % BITMAP_SIZE));
1630 }
1631 
1632 static inline void
vacate_slot(HandleData * handles,guint slot)1633 vacate_slot (HandleData *handles, guint slot) {
1634 	handles->bitmap [slot / BITMAP_SIZE] &= ~(1 << (slot % BITMAP_SIZE));
1635 }
1636 
1637 static inline void
occupy_slot(HandleData * handles,guint slot)1638 occupy_slot (HandleData *handles, guint slot) {
1639 	handles->bitmap [slot / BITMAP_SIZE] |= 1 << (slot % BITMAP_SIZE);
1640 }
1641 
1642 static int
find_first_unset(guint32 bitmap)1643 find_first_unset (guint32 bitmap)
1644 {
1645 	int i;
1646 	for (i = 0; i < 32; ++i) {
1647 		if (!(bitmap & (1 << i)))
1648 			return i;
1649 	}
1650 	return -1;
1651 }
1652 
1653 static void
handle_data_alloc_entries(HandleData * handles)1654 handle_data_alloc_entries (HandleData *handles)
1655 {
1656 	handles->size = 32;
1657 	if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1658 		handles->entries = (void **)g_malloc0 (sizeof (*handles->entries) * handles->size);
1659 		handles->domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * handles->size);
1660 	} else {
1661 		handles->entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * handles->size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1662 	}
1663 	handles->bitmap = (guint32 *)g_malloc0 (handles->size / CHAR_BIT);
1664 }
1665 
1666 static gint
handle_data_next_unset(HandleData * handles)1667 handle_data_next_unset (HandleData *handles)
1668 {
1669 	gint slot;
1670 	for (slot = handles->slot_hint; slot < handles->size / BITMAP_SIZE; ++slot) {
1671 		if (handles->bitmap [slot] == 0xffffffff)
1672 			continue;
1673 		handles->slot_hint = slot;
1674 		return find_first_unset (handles->bitmap [slot]);
1675 	}
1676 	return -1;
1677 }
1678 
1679 static gint
handle_data_first_unset(HandleData * handles)1680 handle_data_first_unset (HandleData *handles)
1681 {
1682 	gint slot;
1683 	for (slot = 0; slot < handles->slot_hint; ++slot) {
1684 		if (handles->bitmap [slot] == 0xffffffff)
1685 			continue;
1686 		handles->slot_hint = slot;
1687 		return find_first_unset (handles->bitmap [slot]);
1688 	}
1689 	return -1;
1690 }
1691 
1692 /* Returns the index of the current slot in the bitmap. */
1693 static void
handle_data_grow(HandleData * handles,gboolean track)1694 handle_data_grow (HandleData *handles, gboolean track)
1695 {
1696 	guint32 *new_bitmap;
1697 	guint32 new_size = handles->size * 2; /* always double: we memset to 0 based on this below */
1698 
1699 	/* resize and copy the bitmap */
1700 	new_bitmap = (guint32 *)g_malloc0 (new_size / CHAR_BIT);
1701 	memcpy (new_bitmap, handles->bitmap, handles->size / CHAR_BIT);
1702 	g_free (handles->bitmap);
1703 	handles->bitmap = new_bitmap;
1704 
1705 	/* resize and copy the entries */
1706 	if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1707 		gpointer *entries;
1708 		guint16 *domain_ids;
1709 		gint i;
1710 		domain_ids = (guint16 *)g_malloc0 (sizeof (*handles->domain_ids) * new_size);
1711 		entries = (void **)g_malloc0 (sizeof (*handles->entries) * new_size);
1712 		memcpy (domain_ids, handles->domain_ids, sizeof (*handles->domain_ids) * handles->size);
1713 		for (i = 0; i < handles->size; ++i) {
1714 			MonoObject *obj = mono_gc_weak_link_get (&(handles->entries [i]));
1715 			if (obj) {
1716 				mono_gc_weak_link_add (&(entries [i]), obj, track);
1717 				mono_gc_weak_link_remove (&(handles->entries [i]), track);
1718 			} else {
1719 				g_assert (!handles->entries [i]);
1720 			}
1721 		}
1722 		g_free (handles->entries);
1723 		g_free (handles->domain_ids);
1724 		handles->entries = entries;
1725 		handles->domain_ids = domain_ids;
1726 	} else {
1727 		gpointer *entries;
1728 		entries = (void **)mono_gc_alloc_fixed (sizeof (*handles->entries) * new_size, NULL, MONO_ROOT_SOURCE_GC_HANDLE, "gc handles table");
1729 		mono_gc_memmove_aligned (entries, handles->entries, sizeof (*handles->entries) * handles->size);
1730 		mono_gc_free_fixed (handles->entries);
1731 		handles->entries = entries;
1732 	}
1733 	handles->slot_hint = handles->size / BITMAP_SIZE;
1734 	handles->size = new_size;
1735 }
1736 
1737 static guint32
alloc_handle(HandleData * handles,MonoObject * obj,gboolean track)1738 alloc_handle (HandleData *handles, MonoObject *obj, gboolean track)
1739 {
1740 	gint slot, i;
1741 	guint32 res;
1742 	lock_handles (handles);
1743 	if (!handles->size)
1744 		handle_data_alloc_entries (handles);
1745 	i = handle_data_next_unset (handles);
1746 	if (i == -1 && handles->slot_hint != 0)
1747 		i = handle_data_first_unset (handles);
1748 	if (i == -1) {
1749 		handle_data_grow (handles, track);
1750 		i = 0;
1751 	}
1752 	slot = handles->slot_hint * BITMAP_SIZE + i;
1753 	occupy_slot (handles, slot);
1754 	handles->entries [slot] = NULL;
1755 	if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1756 		/*FIXME, what to use when obj == null?*/
1757 		handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1758 		if (obj)
1759 			mono_gc_weak_link_add (&(handles->entries [slot]), obj, track);
1760 	} else {
1761 		handles->entries [slot] = obj;
1762 	}
1763 
1764 #ifndef DISABLE_PERFCOUNTERS
1765 	mono_atomic_inc_i32 (&mono_perfcounters->gc_num_handles);
1766 #endif
1767 	unlock_handles (handles);
1768 	res = MONO_GC_HANDLE (slot, handles->type);
1769 	MONO_PROFILER_RAISE (gc_handle_created, (res, handles->type, obj));
1770 	return res;
1771 }
1772 
1773 /**
1774  * mono_gchandle_new:
1775  * \param obj managed object to get a handle for
1776  * \param pinned whether the object should be pinned
1777  *
1778  * This returns a handle that wraps the object, this is used to keep a
1779  * reference to a managed object from the unmanaged world and preventing the
1780  * object from being disposed.
1781  *
1782  * If \p pinned is false the address of the object can not be obtained, if it is
1783  * true the address of the object can be obtained.  This will also pin the
1784  * object so it will not be possible by a moving garbage collector to move the
1785  * object.
1786  *
1787  * \returns a handle that can be used to access the object from
1788  * unmanaged code.
1789  */
1790 guint32
mono_gchandle_new(MonoObject * obj,gboolean pinned)1791 mono_gchandle_new (MonoObject *obj, gboolean pinned)
1792 {
1793 	return alloc_handle (&gc_handles [pinned? HANDLE_PINNED: HANDLE_NORMAL], obj, FALSE);
1794 }
1795 
1796 /**
1797  * mono_gchandle_new_weakref:
1798  * \param obj managed object to get a handle for
1799  * \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.
1800  *
1801  * This returns a weak handle that wraps the object, this is used to
1802  * keep a reference to a managed object from the unmanaged world.
1803  * Unlike the \c mono_gchandle_new the object can be reclaimed by the
1804  * garbage collector.  In this case the value of the GCHandle will be
1805  * set to zero.
1806  *
1807  * If \p track_resurrection is TRUE the object will be tracked through
1808  * finalization and if the object is resurrected during the execution
1809  * of the finalizer, then the returned weakref will continue to hold
1810  * a reference to the object.   If \p track_resurrection is FALSE, then
1811  * the weak reference's target will become NULL as soon as the object
1812  * is passed on to the finalizer.
1813  *
1814  * \returns a handle that can be used to access the object from
1815  * unmanaged code.
1816  */
1817 guint32
mono_gchandle_new_weakref(MonoObject * obj,gboolean track_resurrection)1818 mono_gchandle_new_weakref (MonoObject *obj, gboolean track_resurrection)
1819 {
1820 	return alloc_handle (&gc_handles [track_resurrection? HANDLE_WEAK_TRACK: HANDLE_WEAK], obj, track_resurrection);
1821 }
1822 
1823 /**
1824  * mono_gchandle_get_target:
1825  * \param gchandle a GCHandle's handle.
1826  *
1827  * The handle was previously created by calling \c mono_gchandle_new or
1828  * \c mono_gchandle_new_weakref.
1829  *
1830  * \returns A pointer to the \c MonoObject* represented by the handle or
1831  * NULL for a collected object if using a weakref handle.
1832  */
1833 MonoObject*
mono_gchandle_get_target(guint32 gchandle)1834 mono_gchandle_get_target (guint32 gchandle)
1835 {
1836 	guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1837 	guint type = MONO_GC_HANDLE_TYPE (gchandle);
1838 	HandleData *handles = &gc_handles [type];
1839 	MonoObject *obj = NULL;
1840 	if (type >= HANDLE_TYPE_MAX)
1841 		return NULL;
1842 
1843 	lock_handles (handles);
1844 	if (slot < handles->size && slot_occupied (handles, slot)) {
1845 		if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1846 			obj = mono_gc_weak_link_get (&handles->entries [slot]);
1847 		} else {
1848 			obj = (MonoObject *)handles->entries [slot];
1849 		}
1850 	} else {
1851 		/* print a warning? */
1852 	}
1853 	unlock_handles (handles);
1854 	/*g_print ("get target of entry %d of type %d: %p\n", slot, handles->type, obj);*/
1855 	return obj;
1856 }
1857 
1858 void
mono_gchandle_set_target(guint32 gchandle,MonoObject * obj)1859 mono_gchandle_set_target (guint32 gchandle, MonoObject *obj)
1860 {
1861 	guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1862 	guint type = MONO_GC_HANDLE_TYPE (gchandle);
1863 	HandleData *handles = &gc_handles [type];
1864 	MonoObject *old_obj = NULL;
1865 
1866 	g_assert (type < HANDLE_TYPE_MAX);
1867 	lock_handles (handles);
1868 	if (slot < handles->size && slot_occupied (handles, slot)) {
1869 		if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1870 			old_obj = (MonoObject *)handles->entries [slot];
1871 			if (handles->entries [slot])
1872 				mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1873 			if (obj)
1874 				mono_gc_weak_link_add (&handles->entries [slot], obj, handles->type == HANDLE_WEAK_TRACK);
1875 			/*FIXME, what to use when obj == null?*/
1876 			handles->domain_ids [slot] = (obj ? mono_object_get_domain (obj) : mono_domain_get ())->domain_id;
1877 		} else {
1878 			handles->entries [slot] = obj;
1879 		}
1880 	} else {
1881 		/* print a warning? */
1882 	}
1883 	/*g_print ("changed entry %d of type %d to object %p (in slot: %p)\n", slot, handles->type, obj, handles->entries [slot]);*/
1884 	unlock_handles (handles);
1885 }
1886 
1887 gboolean
mono_gc_is_null(void)1888 mono_gc_is_null (void)
1889 {
1890 	return FALSE;
1891 }
1892 
1893 /**
1894  * mono_gchandle_is_in_domain:
1895  * \param gchandle a GCHandle's handle.
1896  * \param domain An application domain.
1897  *
1898  * Use this function to determine if the \p gchandle points to an
1899  * object allocated in the specified \p domain.
1900  *
1901  * \returns TRUE if the object wrapped by the \p gchandle belongs to the specific \p domain.
1902  */
1903 gboolean
mono_gchandle_is_in_domain(guint32 gchandle,MonoDomain * domain)1904 mono_gchandle_is_in_domain (guint32 gchandle, MonoDomain *domain)
1905 {
1906 	guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1907 	guint type = MONO_GC_HANDLE_TYPE (gchandle);
1908 	HandleData *handles = &gc_handles [type];
1909 	gboolean result = FALSE;
1910 
1911 	if (type >= HANDLE_TYPE_MAX)
1912 		return FALSE;
1913 
1914 	lock_handles (handles);
1915 	if (slot < handles->size && slot_occupied (handles, slot)) {
1916 		if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1917 			result = domain->domain_id == handles->domain_ids [slot];
1918 		} else {
1919 			MonoObject *obj;
1920 			obj = (MonoObject *)handles->entries [slot];
1921 			if (obj == NULL)
1922 				result = TRUE;
1923 			else
1924 				result = domain == mono_object_domain (obj);
1925 		}
1926 	} else {
1927 		/* print a warning? */
1928 	}
1929 	unlock_handles (handles);
1930 	return result;
1931 }
1932 
1933 /**
1934  * mono_gchandle_free:
1935  * \param gchandle a GCHandle's handle.
1936  *
1937  * Frees the \p gchandle handle.  If there are no outstanding
1938  * references, the garbage collector can reclaim the memory of the
1939  * object wrapped.
1940  */
1941 void
mono_gchandle_free(guint32 gchandle)1942 mono_gchandle_free (guint32 gchandle)
1943 {
1944 	guint slot = MONO_GC_HANDLE_SLOT (gchandle);
1945 	guint type = MONO_GC_HANDLE_TYPE (gchandle);
1946 	HandleData *handles = &gc_handles [type];
1947 	if (type >= HANDLE_TYPE_MAX)
1948 		return;
1949 
1950 	lock_handles (handles);
1951 	if (slot < handles->size && slot_occupied (handles, slot)) {
1952 		if (MONO_GC_HANDLE_TYPE_IS_WEAK (handles->type)) {
1953 			if (handles->entries [slot])
1954 				mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1955 		} else {
1956 			handles->entries [slot] = NULL;
1957 		}
1958 		vacate_slot (handles, slot);
1959 	} else {
1960 		/* print a warning? */
1961 	}
1962 #ifndef DISABLE_PERFCOUNTERS
1963 	mono_atomic_dec_i32 (&mono_perfcounters->gc_num_handles);
1964 #endif
1965 	/*g_print ("freed entry %d of type %d\n", slot, handles->type);*/
1966 	unlock_handles (handles);
1967 	MONO_PROFILER_RAISE (gc_handle_deleted, (gchandle, handles->type));
1968 }
1969 
1970 /**
1971  * mono_gchandle_free_domain:
1972  * \param domain domain that is unloading
1973  *
1974  * Function used internally to cleanup any GC handle for objects belonging
1975  * to the specified domain during appdomain unload.
1976  */
1977 void
mono_gchandle_free_domain(MonoDomain * domain)1978 mono_gchandle_free_domain (MonoDomain *domain)
1979 {
1980 	guint type;
1981 
1982 	for (type = HANDLE_TYPE_MIN; type < HANDLE_PINNED; ++type) {
1983 		guint slot;
1984 		HandleData *handles = &gc_handles [type];
1985 		lock_handles (handles);
1986 		for (slot = 0; slot < handles->size; ++slot) {
1987 			if (!slot_occupied (handles, slot))
1988 				continue;
1989 			if (MONO_GC_HANDLE_TYPE_IS_WEAK (type)) {
1990 				if (domain->domain_id == handles->domain_ids [slot]) {
1991 					vacate_slot (handles, slot);
1992 					if (handles->entries [slot])
1993 						mono_gc_weak_link_remove (&handles->entries [slot], handles->type == HANDLE_WEAK_TRACK);
1994 				}
1995 			} else {
1996 				if (handles->entries [slot] && mono_object_domain (handles->entries [slot]) == domain) {
1997 					vacate_slot (handles, slot);
1998 					handles->entries [slot] = NULL;
1999 				}
2000 			}
2001 		}
2002 		unlock_handles (handles);
2003 	}
2004 
2005 }
2006 
2007 void
mono_gc_register_obj_with_weak_fields(void * obj)2008 mono_gc_register_obj_with_weak_fields (void *obj)
2009 {
2010 	g_error ("Weak fields not supported by boehm gc");
2011 }
2012 
2013 #else
2014 
2015 MONO_EMPTY_SOURCE_FILE (boehm_gc);
2016 #endif /* no Boehm GC */
2017