1 /**
2  * \file
3  * GC icalls.
4  *
5  * Author: Paolo Molaro <lupus@ximian.com>
6  *
7  * Copyright 2002-2003 Ximian, Inc (http://www.ximian.com)
8  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
9  * Copyright 2012 Xamarin Inc (http://www.xamarin.com)
10  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11  */
12 
13 #include <config.h>
14 #include <glib.h>
15 #include <string.h>
16 
17 #include <mono/metadata/gc-internals.h>
18 #include <mono/metadata/mono-gc.h>
19 #include <mono/metadata/threads.h>
20 #include <mono/metadata/tabledefs.h>
21 #include <mono/metadata/exception.h>
22 #include <mono/metadata/profiler-private.h>
23 #include <mono/metadata/domain-internals.h>
24 #include <mono/metadata/class-internals.h>
25 #include <mono/metadata/metadata-internals.h>
26 #include <mono/metadata/mono-mlist.h>
27 #include <mono/metadata/threads-types.h>
28 #include <mono/metadata/threadpool.h>
29 #include <mono/sgen/sgen-conf.h>
30 #include <mono/sgen/sgen-gc.h>
31 #include <mono/utils/mono-logger-internals.h>
32 #include <mono/metadata/marshal.h> /* for mono_delegate_free_ftnptr () */
33 #include <mono/metadata/attach.h>
34 #include <mono/metadata/console-io.h>
35 #include <mono/metadata/w32process.h>
36 #include <mono/utils/mono-os-semaphore.h>
37 #include <mono/utils/mono-memory-model.h>
38 #include <mono/utils/mono-counters.h>
39 #include <mono/utils/mono-time.h>
40 #include <mono/utils/dtrace.h>
41 #include <mono/utils/mono-threads.h>
42 #include <mono/utils/mono-threads-coop.h>
43 #include <mono/utils/atomic.h>
44 #include <mono/utils/mono-coop-semaphore.h>
45 #include <mono/utils/hazard-pointer.h>
46 #include <mono/utils/w32api.h>
47 #include <mono/utils/unlocked.h>
48 #include <mono/utils/mono-os-wait.h>
49 
50 #ifndef HOST_WIN32
51 #include <pthread.h>
52 #endif
53 
54 typedef struct DomainFinalizationReq {
55 	gint32 ref;
56 	MonoDomain *domain;
57 	MonoCoopSem done;
58 } DomainFinalizationReq;
59 
60 static gboolean gc_disabled;
61 
62 static gboolean finalizing_root_domain;
63 
64 gboolean log_finalizers;
65 gboolean mono_do_not_finalize;
66 volatile gboolean suspend_finalizers;
67 gchar **mono_do_not_finalize_class_names ;
68 
69 #define mono_finalizer_lock() mono_coop_mutex_lock (&finalizer_mutex)
70 #define mono_finalizer_unlock() mono_coop_mutex_unlock (&finalizer_mutex)
71 static MonoCoopMutex finalizer_mutex;
72 static MonoCoopMutex reference_queue_mutex;
73 
74 static GSList *domains_to_finalize;
75 
76 static gboolean finalizer_thread_exited;
77 /* Uses finalizer_mutex */
78 static MonoCoopCond exited_cond;
79 
80 static MonoInternalThread *gc_thread;
81 
82 #ifdef TARGET_WIN32
83 static HANDLE pending_done_event;
84 #else
85 static gboolean pending_done;
86 static MonoCoopCond pending_done_cond;
87 static MonoCoopMutex pending_done_mutex;
88 #endif
89 
90 static void object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*));
91 
92 static void reference_queue_proccess_all (void);
93 static void mono_reference_queue_cleanup (void);
94 static void reference_queue_clear_for_domain (MonoDomain *domain);
95 
96 
97 static MonoThreadInfoWaitRet
guarded_wait(MonoThreadHandle * thread_handle,guint32 timeout,gboolean alertable)98 guarded_wait (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
99 {
100 	MonoThreadInfoWaitRet result;
101 
102 	MONO_ENTER_GC_SAFE;
103 	result = mono_thread_info_wait_one_handle (thread_handle, timeout, alertable);
104 	MONO_EXIT_GC_SAFE;
105 
106 	return result;
107 }
108 
109 typedef struct {
110 	MonoCoopCond *cond;
111 	MonoCoopMutex *mutex;
112 } BreakCoopAlertableWaitUD;
113 
114 static inline void
break_coop_alertable_wait(gpointer user_data)115 break_coop_alertable_wait (gpointer user_data)
116 {
117 	BreakCoopAlertableWaitUD *ud = (BreakCoopAlertableWaitUD*)user_data;
118 
119 	mono_coop_mutex_lock (ud->mutex);
120 	mono_coop_cond_signal (ud->cond);
121 	mono_coop_mutex_unlock (ud->mutex);
122 
123 	g_free (ud);
124 }
125 
126 /*
127  * coop_cond_timedwait_alertable:
128  *
129  *   Wait on COND/MUTEX. If ALERTABLE is non-null, the wait can be interrupted.
130  * In that case, *ALERTABLE will be set to TRUE, and 0 is returned.
131  */
132 static inline gint
coop_cond_timedwait_alertable(MonoCoopCond * cond,MonoCoopMutex * mutex,guint32 timeout_ms,gboolean * alertable)133 coop_cond_timedwait_alertable (MonoCoopCond *cond, MonoCoopMutex *mutex, guint32 timeout_ms, gboolean *alertable)
134 {
135 	BreakCoopAlertableWaitUD *ud;
136 	int res;
137 
138 	if (alertable) {
139 		ud = g_new0 (BreakCoopAlertableWaitUD, 1);
140 		ud->cond = cond;
141 		ud->mutex = mutex;
142 
143 		mono_thread_info_install_interrupt (break_coop_alertable_wait, ud, alertable);
144 		if (*alertable) {
145 			g_free (ud);
146 			return 0;
147 		}
148 	}
149 	res = mono_coop_cond_timedwait (cond, mutex, timeout_ms);
150 	if (alertable) {
151 		mono_thread_info_uninstall_interrupt (alertable);
152 		if (*alertable)
153 			return 0;
154 		else {
155 			/* the interrupt token has not been taken by another
156 			 * thread, so it's our responsability to free it up. */
157 			g_free (ud);
158 		}
159 	}
160 	return res;
161 }
162 
163 /*
164  * actually, we might want to queue the finalize requests in a separate thread,
165  * but we need to be careful about the execution domain of the thread...
166  */
167 void
mono_gc_run_finalize(void * obj,void * data)168 mono_gc_run_finalize (void *obj, void *data)
169 {
170 	MonoError error;
171 	MonoObject *exc = NULL;
172 	MonoObject *o;
173 #ifndef HAVE_SGEN_GC
174 	MonoObject *o2;
175 #endif
176 	MonoMethod* finalizer = NULL;
177 	MonoDomain *caller_domain = mono_domain_get ();
178 	MonoDomain *domain;
179 	RuntimeInvokeFunction runtime_invoke;
180 
181 	// This function is called from the innards of the GC, so our best alternative for now is to do polling here
182 	mono_threads_safepoint ();
183 
184 	o = (MonoObject*)((char*)obj + GPOINTER_TO_UINT (data));
185 
186 	if (mono_do_not_finalize) {
187 		if (!mono_do_not_finalize_class_names)
188 			return;
189 
190 		size_t namespace_len = strlen (o->vtable->klass->name_space);
191 		for (int i = 0; mono_do_not_finalize_class_names [i]; ++i) {
192 			const char *name = mono_do_not_finalize_class_names [i];
193 			if (strncmp (name, o->vtable->klass->name_space, namespace_len))
194 				break;
195 			if (name [namespace_len] != '.')
196 				break;
197 			if (strcmp (name + namespace_len + 1, o->vtable->klass->name))
198 				break;
199 			return;
200 		}
201 	}
202 
203 	if (log_finalizers)
204 		g_log ("mono-gc-finalizers", G_LOG_LEVEL_DEBUG, "<%s at %p> Starting finalizer checks.", o->vtable->klass->name, o);
205 
206 	if (suspend_finalizers)
207 		return;
208 
209 	domain = o->vtable->domain;
210 
211 #ifndef HAVE_SGEN_GC
212 	mono_domain_finalizers_lock (domain);
213 
214 	o2 = (MonoObject *)g_hash_table_lookup (domain->finalizable_objects_hash, o);
215 
216 	mono_domain_finalizers_unlock (domain);
217 
218 	if (!o2)
219 		/* Already finalized somehow */
220 		return;
221 #endif
222 
223 	/* make sure the finalizer is not called again if the object is resurrected */
224 	object_register_finalizer ((MonoObject *)obj, NULL);
225 
226 	if (log_finalizers)
227 		g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Registered finalizer as processed.", o->vtable->klass->name, o);
228 
229 	if (o->vtable->klass == mono_defaults.internal_thread_class) {
230 		MonoInternalThread *t = (MonoInternalThread*)o;
231 
232 		if (mono_gc_is_finalizer_internal_thread (t))
233 			/* Avoid finalizing ourselves */
234 			return;
235 	}
236 
237 	if (o->vtable->klass->image == mono_defaults.corlib && !strcmp (o->vtable->klass->name, "DynamicMethod") && finalizing_root_domain) {
238 		/*
239 		 * These can't be finalized during unloading/shutdown, since that would
240 		 * free the native code which can still be referenced by other
241 		 * finalizers.
242 		 * FIXME: This is not perfect, objects dying at the same time as
243 		 * dynamic methods can still reference them even when !shutdown.
244 		 */
245 		return;
246 	}
247 
248 	if (mono_runtime_get_no_exec ())
249 		return;
250 
251 	/* speedup later... and use a timeout */
252 	/* g_print ("Finalize run on %p %s.%s\n", o, mono_object_class (o)->name_space, mono_object_class (o)->name); */
253 
254 	/* Use _internal here, since this thread can enter a doomed appdomain */
255 	mono_domain_set_internal (mono_object_domain (o));
256 
257 	/* delegates that have a native function pointer allocated are
258 	 * registered for finalization, but they don't have a Finalize
259 	 * method, because in most cases it's not needed and it's just a waste.
260 	 */
261 	if (o->vtable->klass->delegate) {
262 		MonoDelegate* del = (MonoDelegate*)o;
263 		if (del->delegate_trampoline)
264 			mono_delegate_free_ftnptr ((MonoDelegate*)o);
265 		mono_domain_set_internal (caller_domain);
266 		return;
267 	}
268 
269 	finalizer = mono_class_get_finalizer (o->vtable->klass);
270 
271 	/* If object has a CCW but has no finalizer, it was only
272 	 * registered for finalization in order to free the CCW.
273 	 * Else it needs the regular finalizer run.
274 	 * FIXME: what to do about ressurection and suppression
275 	 * of finalizer on object with CCW.
276 	 */
277 	if (mono_marshal_free_ccw (o) && !finalizer) {
278 		mono_domain_set_internal (caller_domain);
279 		return;
280 	}
281 
282 	/*
283 	 * To avoid the locking plus the other overhead of mono_runtime_invoke_checked (),
284 	 * create and precompile a wrapper which calls the finalize method using
285 	 * a CALLVIRT.
286 	 */
287 	if (log_finalizers)
288 		g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Compiling finalizer.", o->vtable->klass->name, o);
289 
290 	if (!domain->finalize_runtime_invoke) {
291 		MonoMethod *invoke = mono_marshal_get_runtime_invoke (mono_class_get_method_from_name_flags (mono_defaults.object_class, "Finalize", 0, 0), TRUE);
292 
293 		domain->finalize_runtime_invoke = mono_compile_method_checked (invoke, &error);
294 		mono_error_assert_ok (&error); /* expect this not to fail */
295 	}
296 
297 	runtime_invoke = (RuntimeInvokeFunction)domain->finalize_runtime_invoke;
298 
299 	mono_runtime_class_init_full (o->vtable, &error);
300 	goto_if_nok (&error, unhandled_error);
301 
302 	if (G_UNLIKELY (MONO_GC_FINALIZE_INVOKE_ENABLED ())) {
303 		MONO_GC_FINALIZE_INVOKE ((unsigned long)o, mono_object_get_size (o),
304 				o->vtable->klass->name_space, o->vtable->klass->name);
305 	}
306 
307 	if (log_finalizers)
308 		g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Calling finalizer.", o->vtable->klass->name, o);
309 
310 	MONO_PROFILER_RAISE (gc_finalizing_object, (o));
311 
312 	runtime_invoke (o, NULL, &exc, NULL);
313 
314 	MONO_PROFILER_RAISE (gc_finalized_object, (o));
315 
316 	if (log_finalizers)
317 		g_log ("mono-gc-finalizers", G_LOG_LEVEL_MESSAGE, "<%s at %p> Returned from finalizer.", o->vtable->klass->name, o);
318 
319 unhandled_error:
320 	if (!is_ok (&error))
321 		exc = (MonoObject*)mono_error_convert_to_exception (&error);
322 	if (exc)
323 		mono_thread_internal_unhandled_exception (exc);
324 
325 	mono_domain_set_internal (caller_domain);
326 }
327 
328 /*
329  * Some of our objects may point to a different address than the address returned by GC_malloc()
330  * (because of the GetHashCode hack), but we need to pass the real address to register_finalizer.
331  * This also means that in the callback we need to adjust the pointer to get back the real
332  * MonoObject*.
333  * We also need to be consistent in the use of the GC_debug* variants of malloc and register_finalizer,
334  * since that, too, can cause the underlying pointer to be offset.
335  */
336 static void
object_register_finalizer(MonoObject * obj,void (* callback)(void *,void *))337 object_register_finalizer (MonoObject *obj, void (*callback)(void *, void*))
338 {
339 	MonoDomain *domain;
340 
341 	g_assert (obj != NULL);
342 
343 	domain = obj->vtable->domain;
344 
345 #if HAVE_BOEHM_GC
346 	if (mono_domain_is_unloading (domain) && (callback != NULL))
347 		/*
348 		 * Can't register finalizers in a dying appdomain, since they
349 		 * could be invoked after the appdomain has been unloaded.
350 		 */
351 		return;
352 
353 	mono_domain_finalizers_lock (domain);
354 
355 	if (callback)
356 		g_hash_table_insert (domain->finalizable_objects_hash, obj, obj);
357 	else
358 		g_hash_table_remove (domain->finalizable_objects_hash, obj);
359 
360 	mono_domain_finalizers_unlock (domain);
361 
362 	mono_gc_register_for_finalization (obj, callback);
363 #elif defined(HAVE_SGEN_GC)
364 	/*
365 	 * If we register finalizers for domains that are unloading we might
366 	 * end up running them while or after the domain is being cleared, so
367 	 * the objects will not be valid anymore.
368 	 */
369 	if (!mono_domain_is_unloading (domain))
370 		mono_gc_register_for_finalization (obj, callback);
371 #endif
372 }
373 
374 /**
375  * mono_object_register_finalizer:
376  * \param obj object to register
377  *
378  * Records that object \p obj has a finalizer, this will call the
379  * Finalize method when the garbage collector disposes the object.
380  *
381  */
382 void
mono_object_register_finalizer(MonoObject * obj)383 mono_object_register_finalizer (MonoObject *obj)
384 {
385 	/* g_print ("Registered finalizer on %p %s.%s\n", obj, mono_object_class (obj)->name_space, mono_object_class (obj)->name); */
386 	object_register_finalizer (obj, mono_gc_run_finalize);
387 }
388 
389 /**
390  * mono_domain_finalize:
391  * \param domain the domain to finalize
392  * \param timeout msecs to wait for the finalization to complete, \c -1 to wait indefinitely
393  *
394  * Request finalization of all finalizable objects inside \p domain. Wait
395  * \p timeout msecs for the finalization to complete.
396  *
397  * \returns TRUE if succeeded, FALSE if there was a timeout
398  */
399 gboolean
mono_domain_finalize(MonoDomain * domain,guint32 timeout)400 mono_domain_finalize (MonoDomain *domain, guint32 timeout)
401 {
402 	DomainFinalizationReq *req;
403 	MonoInternalThread *thread = mono_thread_internal_current ();
404 	gint res;
405 	gboolean ret;
406 	gint64 start;
407 
408 	if (mono_thread_internal_current () == gc_thread)
409 		/* We are called from inside a finalizer, not much we can do here */
410 		return FALSE;
411 
412 	/*
413 	 * No need to create another thread 'cause the finalizer thread
414 	 * is still working and will take care of running the finalizers
415 	 */
416 
417 	if (gc_disabled)
418 		return TRUE;
419 
420 	/* We don't support domain finalization without a GC */
421 	if (mono_gc_is_null ())
422 		return FALSE;
423 
424 	mono_gc_collect (mono_gc_max_generation ());
425 
426 	req = g_new0 (DomainFinalizationReq, 1);
427 	req->ref = 2;
428 	req->domain = domain;
429 	mono_coop_sem_init (&req->done, 0);
430 
431 	if (domain == mono_get_root_domain ())
432 		finalizing_root_domain = TRUE;
433 
434 	mono_finalizer_lock ();
435 
436 	domains_to_finalize = g_slist_append (domains_to_finalize, req);
437 
438 	mono_finalizer_unlock ();
439 
440 	/* Tell the finalizer thread to finalize this appdomain */
441 	mono_gc_finalize_notify ();
442 
443 	if (timeout == -1)
444 		timeout = MONO_INFINITE_WAIT;
445 	if (timeout != MONO_INFINITE_WAIT)
446 		start = mono_msec_ticks ();
447 
448 	ret = TRUE;
449 
450 	for (;;) {
451 		if (timeout == MONO_INFINITE_WAIT) {
452 			res = mono_coop_sem_wait (&req->done, MONO_SEM_FLAGS_ALERTABLE);
453 		} else {
454 			gint64 elapsed = mono_msec_ticks () - start;
455 			if (elapsed >= timeout) {
456 				ret = FALSE;
457 				break;
458 			}
459 
460 			res = mono_coop_sem_timedwait (&req->done, timeout - elapsed, MONO_SEM_FLAGS_ALERTABLE);
461 		}
462 
463 		if (res == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
464 			break;
465 		} else if (res == MONO_SEM_TIMEDWAIT_RET_ALERTED) {
466 			if ((thread->state & (ThreadState_AbortRequested | ThreadState_SuspendRequested)) != 0) {
467 				ret = FALSE;
468 				break;
469 			}
470 		} else if (res == MONO_SEM_TIMEDWAIT_RET_TIMEDOUT) {
471 			ret = FALSE;
472 			break;
473 		} else {
474 			g_error ("%s: unknown result %d", __func__, res);
475 		}
476 	}
477 
478 	if (!ret) {
479 		/* Try removing the req from domains_to_finalize:
480 		 *  - if it's not found: the domain is being finalized,
481 		 *     so we the ref count is already decremented
482 		 *  - if it's found: the domain is not yet being finalized,
483 		 *     so we can safely decrement the ref */
484 
485 		gboolean found;
486 
487 		mono_finalizer_lock ();
488 
489 		found = g_slist_index (domains_to_finalize, req) != -1;
490 		if (found)
491 			domains_to_finalize = g_slist_remove (domains_to_finalize, req);
492 
493 		mono_finalizer_unlock ();
494 
495 		if (found) {
496 			/* We have to decrement it wherever we
497 			 * remove it from domains_to_finalize */
498 			if (mono_atomic_dec_i32 (&req->ref) != 1)
499 				g_error ("%s: req->ref should be 1, as we are the first one to decrement it", __func__);
500 		}
501 
502 		goto done;
503 	}
504 
505 done:
506 	if (mono_atomic_dec_i32 (&req->ref) == 0) {
507 		mono_coop_sem_destroy (&req->done);
508 		g_free (req);
509 	}
510 
511 	return ret;
512 }
513 
514 void
ves_icall_System_GC_InternalCollect(int generation)515 ves_icall_System_GC_InternalCollect (int generation)
516 {
517 	mono_gc_collect (generation);
518 }
519 
520 gint64
ves_icall_System_GC_GetTotalMemory(MonoBoolean forceCollection)521 ves_icall_System_GC_GetTotalMemory (MonoBoolean forceCollection)
522 {
523 	if (forceCollection)
524 		mono_gc_collect (mono_gc_max_generation ());
525 	return mono_gc_get_used_size ();
526 }
527 
528 void
ves_icall_System_GC_KeepAlive(MonoObject * obj)529 ves_icall_System_GC_KeepAlive (MonoObject *obj)
530 {
531 	/*
532 	 * Does nothing.
533 	 */
534 }
535 
536 void
ves_icall_System_GC_ReRegisterForFinalize(MonoObject * obj)537 ves_icall_System_GC_ReRegisterForFinalize (MonoObject *obj)
538 {
539 	MONO_CHECK_ARG_NULL (obj,);
540 
541 	object_register_finalizer (obj, mono_gc_run_finalize);
542 }
543 
544 void
ves_icall_System_GC_SuppressFinalize(MonoObject * obj)545 ves_icall_System_GC_SuppressFinalize (MonoObject *obj)
546 {
547 	MONO_CHECK_ARG_NULL (obj,);
548 
549 	/* delegates have no finalizers, but we register them to deal with the
550 	 * unmanaged->managed trampoline. We don't let the user suppress it
551 	 * otherwise we'd leak it.
552 	 */
553 	if (obj->vtable->klass->delegate)
554 		return;
555 
556 	/* FIXME: Need to handle case where obj has COM Callable Wrapper
557 	 * generated for it that needs cleaned up, but user wants to suppress
558 	 * their derived object finalizer. */
559 
560 	object_register_finalizer (obj, NULL);
561 }
562 
563 void
ves_icall_System_GC_WaitForPendingFinalizers(void)564 ves_icall_System_GC_WaitForPendingFinalizers (void)
565 {
566 	if (mono_gc_is_null ())
567 		return;
568 
569 	if (!mono_gc_pending_finalizers ())
570 		return;
571 
572 	if (mono_thread_internal_current () == gc_thread)
573 		/* Avoid deadlocks */
574 		return;
575 
576 	/*
577 	If the finalizer thread is not live, lets pretend no finalizers are pending since the current thread might
578 	be the one responsible for starting it up.
579 	*/
580 	if (gc_thread == NULL)
581 		return;
582 
583 #ifdef TARGET_WIN32
584 	ResetEvent (pending_done_event);
585 	mono_gc_finalize_notify ();
586 	/* g_print ("Waiting for pending finalizers....\n"); */
587 	MONO_ENTER_GC_SAFE;
588 	mono_win32_wait_for_single_object_ex (pending_done_event, INFINITE, TRUE);
589 	MONO_EXIT_GC_SAFE;
590 	/* g_print ("Done pending....\n"); */
591 #else
592 	gboolean alerted = FALSE;
593 	mono_coop_mutex_lock (&pending_done_mutex);
594 	pending_done = FALSE;
595 	mono_gc_finalize_notify ();
596 	while (!pending_done) {
597 		coop_cond_timedwait_alertable (&pending_done_cond, &pending_done_mutex, MONO_INFINITE_WAIT, &alerted);
598 		if (alerted)
599 			break;
600 	}
601 	mono_coop_mutex_unlock (&pending_done_mutex);
602 #endif
603 }
604 
605 void
ves_icall_System_GC_register_ephemeron_array(MonoObject * array)606 ves_icall_System_GC_register_ephemeron_array (MonoObject *array)
607 {
608 #ifdef HAVE_SGEN_GC
609 	if (!mono_gc_ephemeron_array_add (array)) {
610 		mono_set_pending_exception (mono_object_domain (array)->out_of_memory_ex);
611 		return;
612 	}
613 #endif
614 }
615 
616 MonoObject*
ves_icall_System_GC_get_ephemeron_tombstone(void)617 ves_icall_System_GC_get_ephemeron_tombstone (void)
618 {
619 	return mono_domain_get ()->ephemeron_tombstone;
620 }
621 
622 MonoObject *
ves_icall_System_GCHandle_GetTarget(guint32 handle)623 ves_icall_System_GCHandle_GetTarget (guint32 handle)
624 {
625 	return mono_gchandle_get_target (handle);
626 }
627 
628 /*
629  * if type == -1, change the target of the handle, otherwise allocate a new handle.
630  */
631 guint32
ves_icall_System_GCHandle_GetTargetHandle(MonoObject * obj,guint32 handle,gint32 type)632 ves_icall_System_GCHandle_GetTargetHandle (MonoObject *obj, guint32 handle, gint32 type)
633 {
634 	if (type == -1) {
635 		mono_gchandle_set_target (handle, obj);
636 		/* the handle doesn't change */
637 		return handle;
638 	}
639 	switch (type) {
640 	case HANDLE_WEAK:
641 		return mono_gchandle_new_weakref (obj, FALSE);
642 	case HANDLE_WEAK_TRACK:
643 		return mono_gchandle_new_weakref (obj, TRUE);
644 	case HANDLE_NORMAL:
645 		return mono_gchandle_new (obj, FALSE);
646 	case HANDLE_PINNED:
647 		return mono_gchandle_new (obj, TRUE);
648 	default:
649 		g_assert_not_reached ();
650 	}
651 	return 0;
652 }
653 
654 void
ves_icall_System_GCHandle_FreeHandle(guint32 handle)655 ves_icall_System_GCHandle_FreeHandle (guint32 handle)
656 {
657 	mono_gchandle_free (handle);
658 }
659 
660 gpointer
ves_icall_System_GCHandle_GetAddrOfPinnedObject(guint32 handle)661 ves_icall_System_GCHandle_GetAddrOfPinnedObject (guint32 handle)
662 {
663 	MonoObject *obj;
664 
665 	if (MONO_GC_HANDLE_TYPE (handle) != HANDLE_PINNED)
666 		return (gpointer)-2;
667 	obj = mono_gchandle_get_target (handle);
668 	if (obj) {
669 		MonoClass *klass = mono_object_class (obj);
670 		if (klass == mono_defaults.string_class) {
671 			return mono_string_chars ((MonoString*)obj);
672 		} else if (klass->rank) {
673 			return mono_array_addr ((MonoArray*)obj, char, 0);
674 		} else {
675 			/* the C# code will check and throw the exception */
676 			/* FIXME: missing !klass->blittable test, see bug #61134 */
677 			if (mono_class_is_auto_layout (klass))
678 				return (gpointer)-1;
679 			return (char*)obj + sizeof (MonoObject);
680 		}
681 	}
682 	return NULL;
683 }
684 
685 MonoBoolean
mono_gc_GCHandle_CheckCurrentDomain(guint32 gchandle)686 mono_gc_GCHandle_CheckCurrentDomain (guint32 gchandle)
687 {
688 	return mono_gchandle_is_in_domain (gchandle, mono_domain_get ());
689 }
690 
691 static MonoCoopSem finalizer_sem;
692 static volatile gboolean finished;
693 
694 /*
695  * mono_gc_finalize_notify:
696  *
697  *   Notify the finalizer thread that finalizers etc.
698  * are available to be processed.
699  * This is async signal safe.
700  */
701 void
mono_gc_finalize_notify(void)702 mono_gc_finalize_notify (void)
703 {
704 #ifdef DEBUG
705 	g_message ( "%s: prodding finalizer", __func__);
706 #endif
707 
708 	if (mono_gc_is_null ())
709 		return;
710 
711 	mono_coop_sem_post (&finalizer_sem);
712 }
713 
714 /*
715 This is the number of entries allowed in the hazard free queue before
716 we explicitly cycle the finalizer thread to trigger pumping the queue.
717 
718 It was picked empirically by running the corlib test suite in a stress
719 scenario where all hazard entries are queued.
720 
721 In this extreme scenario we double the number of times we cycle the finalizer
722 thread compared to just GC calls.
723 
724 Entries are usually in the order of 100's of bytes each, so we're limiting
725 floating garbage to be in the order of a dozen kb.
726 */
727 static gboolean finalizer_thread_pulsed;
728 #define HAZARD_QUEUE_OVERFLOW_SIZE 20
729 
730 static void
hazard_free_queue_is_too_big(size_t size)731 hazard_free_queue_is_too_big (size_t size)
732 {
733 	if (size < HAZARD_QUEUE_OVERFLOW_SIZE)
734 		return;
735 
736 	if (finalizer_thread_pulsed || mono_atomic_cas_i32 (&finalizer_thread_pulsed, TRUE, FALSE))
737 		return;
738 
739 	mono_gc_finalize_notify ();
740 }
741 
742 static void
hazard_free_queue_pump(void)743 hazard_free_queue_pump (void)
744 {
745 	mono_thread_hazardous_try_free_all ();
746 	finalizer_thread_pulsed = FALSE;
747 }
748 
749 #ifdef HAVE_BOEHM_GC
750 
751 static void
collect_objects(gpointer key,gpointer value,gpointer user_data)752 collect_objects (gpointer key, gpointer value, gpointer user_data)
753 {
754 	GPtrArray *arr = (GPtrArray*)user_data;
755 	g_ptr_array_add (arr, key);
756 }
757 
758 #endif
759 
760 /*
761  * finalize_domain_objects:
762  *
763  *  Run the finalizers of all finalizable objects in req->domain.
764  */
765 static void
finalize_domain_objects(void)766 finalize_domain_objects (void)
767 {
768 	DomainFinalizationReq *req = NULL;
769 	MonoDomain *domain;
770 
771 	if (UnlockedReadPointer ((gpointer)&domains_to_finalize)) {
772 		mono_finalizer_lock ();
773 		if (domains_to_finalize) {
774 			req = (DomainFinalizationReq *)domains_to_finalize->data;
775 			domains_to_finalize = g_slist_remove (domains_to_finalize, req);
776 		}
777 		mono_finalizer_unlock ();
778 	}
779 
780 	if (!req)
781 		return;
782 
783 	domain = req->domain;
784 
785 	/* Process finalizers which are already in the queue */
786 	mono_gc_invoke_finalizers ();
787 
788 #ifdef HAVE_BOEHM_GC
789 	while (g_hash_table_size (domain->finalizable_objects_hash) > 0) {
790 		int i;
791 		GPtrArray *objs;
792 		/*
793 		 * Since the domain is unloading, nobody is allowed to put
794 		 * new entries into the hash table. But finalize_object might
795 		 * remove entries from the hash table, so we make a copy.
796 		 */
797 		objs = g_ptr_array_new ();
798 		g_hash_table_foreach (domain->finalizable_objects_hash, collect_objects, objs);
799 		/* printf ("FINALIZING %d OBJECTS.\n", objs->len); */
800 
801 		for (i = 0; i < objs->len; ++i) {
802 			MonoObject *o = (MonoObject*)g_ptr_array_index (objs, i);
803 			/* FIXME: Avoid finalizing threads, etc */
804 			mono_gc_run_finalize (o, 0);
805 		}
806 
807 		g_ptr_array_free (objs, TRUE);
808 	}
809 #elif defined(HAVE_SGEN_GC)
810 	mono_gc_finalize_domain (domain);
811 	mono_gc_invoke_finalizers ();
812 #endif
813 
814 	/* cleanup the reference queue */
815 	reference_queue_clear_for_domain (domain);
816 
817 	/* printf ("DONE.\n"); */
818 	mono_coop_sem_post (&req->done);
819 
820 	if (mono_atomic_dec_i32 (&req->ref) == 0) {
821 		/* mono_domain_finalize already returned, and
822 		 * doesn't hold a reference to req anymore. */
823 		mono_coop_sem_destroy (&req->done);
824 		g_free (req);
825 	}
826 }
827 
828 static gsize WINAPI
finalizer_thread(gpointer unused)829 finalizer_thread (gpointer unused)
830 {
831 	MonoError error;
832 	gboolean wait = TRUE;
833 
834 	MonoString *finalizer = mono_string_new_checked (mono_get_root_domain (), "Finalizer", &error);
835 	mono_error_assert_ok (&error);
836 	mono_thread_set_name_internal (mono_thread_internal_current (), finalizer, FALSE, FALSE, &error);
837 	mono_error_assert_ok (&error);
838 
839 	/* Register a hazard free queue pump callback */
840 	mono_hazard_pointer_install_free_queue_size_callback (hazard_free_queue_is_too_big);
841 
842 	while (!finished) {
843 		/* Wait to be notified that there's at least one
844 		 * finaliser to run
845 		 */
846 
847 		g_assert (mono_domain_get () == mono_get_root_domain ());
848 		mono_gc_set_skip_thread (TRUE);
849 
850 		if (wait) {
851 			/* An alertable wait is required so this thread can be suspended on windows */
852 			mono_coop_sem_wait (&finalizer_sem, MONO_SEM_FLAGS_ALERTABLE);
853 		}
854 		wait = TRUE;
855 
856 		mono_gc_set_skip_thread (FALSE);
857 
858 		mono_threads_perform_thread_dump ();
859 
860 		mono_console_handle_async_ops ();
861 
862 		mono_attach_maybe_start ();
863 
864 		finalize_domain_objects ();
865 
866 		MONO_PROFILER_RAISE (gc_finalizing, ());
867 
868 		/* If finished == TRUE, mono_gc_cleanup has been called (from mono_runtime_cleanup),
869 		 * before the domain is unloaded.
870 		 */
871 		mono_gc_invoke_finalizers ();
872 
873 		MONO_PROFILER_RAISE (gc_finalized, ());
874 
875 		mono_threads_join_threads ();
876 
877 		reference_queue_proccess_all ();
878 
879 		mono_w32process_signal_finished ();
880 
881 		hazard_free_queue_pump ();
882 
883 		/* Avoid posting the pending done event until there are pending finalizers */
884 		if (mono_coop_sem_timedwait (&finalizer_sem, 0, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS) {
885 			/* Don't wait again at the start of the loop */
886 			wait = FALSE;
887 		} else {
888 #ifdef TARGET_WIN32
889 			SetEvent (pending_done_event);
890 #else
891 			mono_coop_mutex_lock (&pending_done_mutex);
892 			pending_done = TRUE;
893 			mono_coop_cond_signal (&pending_done_cond);
894 			mono_coop_mutex_unlock (&pending_done_mutex);
895 #endif
896 		}
897 	}
898 
899 	mono_finalizer_lock ();
900 	finalizer_thread_exited = TRUE;
901 	mono_coop_cond_signal (&exited_cond);
902 	mono_finalizer_unlock ();
903 
904 	return 0;
905 }
906 
907 #ifndef LAZY_GC_THREAD_CREATION
908 static
909 #endif
910 void
mono_gc_init_finalizer_thread(void)911 mono_gc_init_finalizer_thread (void)
912 {
913 	MonoError error;
914 	gc_thread = mono_thread_create_internal (mono_domain_get (), finalizer_thread, NULL, MONO_THREAD_CREATE_FLAGS_NONE, &error);
915 	mono_error_assert_ok (&error);
916 }
917 
918 void
mono_gc_init(void)919 mono_gc_init (void)
920 {
921 	mono_coop_mutex_init_recursive (&finalizer_mutex);
922 	mono_coop_mutex_init_recursive (&reference_queue_mutex);
923 
924 	mono_counters_register ("Minor GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.minor_gc_count);
925 	mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.major_gc_count);
926 	mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
927 	mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
928 	mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
929 
930 	mono_gc_base_init ();
931 
932 	if (mono_gc_is_disabled ()) {
933 		gc_disabled = TRUE;
934 		return;
935 	}
936 
937 #ifdef TARGET_WIN32
938 	pending_done_event = CreateEvent (NULL, TRUE, FALSE, NULL);
939 	g_assert (pending_done_event);
940 #else
941 	mono_coop_cond_init (&pending_done_cond);
942 	mono_coop_mutex_init (&pending_done_mutex);
943 #endif
944 
945 	mono_coop_cond_init (&exited_cond);
946 	mono_coop_sem_init (&finalizer_sem, 0);
947 
948 #ifndef LAZY_GC_THREAD_CREATION
949 	mono_gc_init_finalizer_thread ();
950 #endif
951 }
952 
953 void
mono_gc_cleanup(void)954 mono_gc_cleanup (void)
955 {
956 #ifdef DEBUG
957 	g_message ("%s: cleaning up finalizer", __func__);
958 #endif
959 
960 	if (mono_gc_is_null ())
961 		return;
962 
963 	if (!gc_disabled) {
964 		finished = TRUE;
965 		if (mono_thread_internal_current () != gc_thread) {
966 			int ret;
967 			gint64 start;
968 			const gint64 timeout = 40 * 1000;
969 
970 			mono_gc_finalize_notify ();
971 
972 			start = mono_msec_ticks ();
973 
974 			/* Finishing the finalizer thread, so wait a little bit... */
975 			/* MS seems to wait for about 2 seconds per finalizer thread */
976 			/* and 40 seconds for all finalizers to finish */
977 			for (;;) {
978 				gint64 elapsed;
979 
980 				if (finalizer_thread_exited) {
981 					/* Wait for the thread to actually exit. We don't want the wait
982 					 * to be alertable, because we assert on the result to be SUCCESS_0 */
983 					ret = guarded_wait (gc_thread->handle, MONO_INFINITE_WAIT, FALSE);
984 					g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
985 
986 					mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid)));
987 					break;
988 				}
989 
990 				elapsed = mono_msec_ticks () - start;
991 				if (elapsed >= timeout) {
992 					/* timeout */
993 
994 					/* Set a flag which the finalizer thread can check */
995 					suspend_finalizers = TRUE;
996 					mono_gc_suspend_finalizers ();
997 
998 					/* Try to abort the thread, in the hope that it is running managed code */
999 					mono_thread_internal_abort (gc_thread, FALSE);
1000 
1001 					/* Wait for it to stop */
1002 					ret = guarded_wait (gc_thread->handle, 100, FALSE);
1003 					if (ret == MONO_THREAD_INFO_WAIT_RET_TIMEOUT) {
1004 						/* The finalizer thread refused to exit, suspend it forever. */
1005 						mono_thread_internal_suspend_for_shutdown (gc_thread);
1006 						break;
1007 					}
1008 
1009 					g_assert (ret == MONO_THREAD_INFO_WAIT_RET_SUCCESS_0);
1010 
1011 					mono_threads_add_joinable_thread ((gpointer)(MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid)));
1012 					break;
1013 				}
1014 
1015 				mono_finalizer_lock ();
1016 				if (!finalizer_thread_exited)
1017 					mono_coop_cond_timedwait (&exited_cond, &finalizer_mutex, timeout - elapsed);
1018 				mono_finalizer_unlock ();
1019 			}
1020 		}
1021 		gc_thread = NULL;
1022 		mono_gc_base_cleanup ();
1023 	}
1024 
1025 	mono_reference_queue_cleanup ();
1026 
1027 	mono_coop_mutex_destroy (&finalizer_mutex);
1028 	mono_coop_mutex_destroy (&reference_queue_mutex);
1029 }
1030 
1031 gboolean
mono_gc_is_finalizer_internal_thread(MonoInternalThread * thread)1032 mono_gc_is_finalizer_internal_thread (MonoInternalThread *thread)
1033 {
1034 	return thread == gc_thread;
1035 }
1036 
1037 /**
1038  * mono_gc_is_finalizer_thread:
1039  * \param thread the thread to test.
1040  *
1041  * In Mono objects are finalized asynchronously on a separate thread.
1042  * This routine tests whether the \p thread argument represents the
1043  * finalization thread.
1044  *
1045  * \returns TRUE if \p thread is the finalization thread.
1046  */
1047 gboolean
mono_gc_is_finalizer_thread(MonoThread * thread)1048 mono_gc_is_finalizer_thread (MonoThread *thread)
1049 {
1050 	return mono_gc_is_finalizer_internal_thread (thread->internal_thread);
1051 }
1052 
1053 #if defined(__MACH__)
1054 static pthread_t mach_exception_thread;
1055 
1056 void
mono_gc_register_mach_exception_thread(pthread_t thread)1057 mono_gc_register_mach_exception_thread (pthread_t thread)
1058 {
1059 	mach_exception_thread = thread;
1060 }
1061 
1062 pthread_t
mono_gc_get_mach_exception_thread(void)1063 mono_gc_get_mach_exception_thread (void)
1064 {
1065 	return mach_exception_thread;
1066 }
1067 #endif
1068 
1069 static MonoReferenceQueue *ref_queues;
1070 
1071 static void
ref_list_remove_element(RefQueueEntry ** prev,RefQueueEntry * element)1072 ref_list_remove_element (RefQueueEntry **prev, RefQueueEntry *element)
1073 {
1074 	do {
1075 		/* Guard if head is changed concurrently. */
1076 		while (*prev != element)
1077 			prev = &(*prev)->next;
1078 	} while (prev && mono_atomic_cas_ptr ((volatile gpointer *)prev, element->next, element) != element);
1079 }
1080 
1081 static void
ref_list_push(RefQueueEntry ** head,RefQueueEntry * value)1082 ref_list_push (RefQueueEntry **head, RefQueueEntry *value)
1083 {
1084 	RefQueueEntry *current;
1085 	do {
1086 		current = *head;
1087 		value->next = current;
1088 		STORE_STORE_FENCE; /*Must make sure the previous store is visible before the CAS. */
1089 	} while (mono_atomic_cas_ptr ((volatile gpointer *)head, value, current) != current);
1090 }
1091 
1092 static void
reference_queue_proccess(MonoReferenceQueue * queue)1093 reference_queue_proccess (MonoReferenceQueue *queue)
1094 {
1095 	RefQueueEntry **iter = &queue->queue;
1096 	RefQueueEntry *entry;
1097 	while ((entry = *iter)) {
1098 		if (queue->should_be_deleted || !mono_gchandle_get_target (entry->gchandle)) {
1099 			mono_gchandle_free ((guint32)entry->gchandle);
1100 			ref_list_remove_element (iter, entry);
1101 			queue->callback (entry->user_data);
1102 			g_free (entry);
1103 		} else {
1104 			iter = &entry->next;
1105 		}
1106 	}
1107 }
1108 
1109 static void
reference_queue_proccess_all(void)1110 reference_queue_proccess_all (void)
1111 {
1112 	MonoReferenceQueue **iter;
1113 	MonoReferenceQueue *queue = ref_queues;
1114 	for (; queue; queue = queue->next)
1115 		reference_queue_proccess (queue);
1116 
1117 restart:
1118 	mono_coop_mutex_lock (&reference_queue_mutex);
1119 	for (iter = &ref_queues; *iter;) {
1120 		queue = *iter;
1121 		if (!queue->should_be_deleted) {
1122 			iter = &queue->next;
1123 			continue;
1124 		}
1125 		if (queue->queue) {
1126 			mono_coop_mutex_unlock (&reference_queue_mutex);
1127 			reference_queue_proccess (queue);
1128 			goto restart;
1129 		}
1130 		*iter = queue->next;
1131 		g_free (queue);
1132 	}
1133 	mono_coop_mutex_unlock (&reference_queue_mutex);
1134 }
1135 
1136 static void
mono_reference_queue_cleanup(void)1137 mono_reference_queue_cleanup (void)
1138 {
1139 	MonoReferenceQueue *queue = ref_queues;
1140 	for (; queue; queue = queue->next)
1141 		queue->should_be_deleted = TRUE;
1142 	reference_queue_proccess_all ();
1143 }
1144 
1145 static void
reference_queue_clear_for_domain(MonoDomain * domain)1146 reference_queue_clear_for_domain (MonoDomain *domain)
1147 {
1148 	MonoReferenceQueue *queue = ref_queues;
1149 	for (; queue; queue = queue->next) {
1150 		RefQueueEntry **iter = &queue->queue;
1151 		RefQueueEntry *entry;
1152 		while ((entry = *iter)) {
1153 			if (entry->domain == domain) {
1154 				mono_gchandle_free ((guint32)entry->gchandle);
1155 				ref_list_remove_element (iter, entry);
1156 				queue->callback (entry->user_data);
1157 				g_free (entry);
1158 			} else {
1159 				iter = &entry->next;
1160 			}
1161 		}
1162 	}
1163 }
1164 /**
1165  * mono_gc_reference_queue_new:
1166  * \param callback callback used when processing collected entries.
1167  *
1168  * Create a new reference queue used to process collected objects.
1169  * A reference queue let you add a pair of (managed object, user data)
1170  * using the \c mono_gc_reference_queue_add method.
1171  *
1172  * Once the managed object is collected \p callback will be called
1173  * in the finalizer thread with 'user data' as argument.
1174  *
1175  * The callback is called from the finalizer thread without any locks held.
1176  * When an AppDomain is unloaded, all callbacks for objects belonging to it
1177  * will be invoked.
1178  *
1179  * \returns the new queue.
1180  */
1181 MonoReferenceQueue*
mono_gc_reference_queue_new(mono_reference_queue_callback callback)1182 mono_gc_reference_queue_new (mono_reference_queue_callback callback)
1183 {
1184 	MonoReferenceQueue *res = g_new0 (MonoReferenceQueue, 1);
1185 	res->callback = callback;
1186 
1187 	mono_coop_mutex_lock (&reference_queue_mutex);
1188 	res->next = ref_queues;
1189 	ref_queues = res;
1190 	mono_coop_mutex_unlock (&reference_queue_mutex);
1191 
1192 	return res;
1193 }
1194 
1195 /**
1196  * mono_gc_reference_queue_add:
1197  * \param queue the queue to add the reference to.
1198  * \param obj the object to be watched for collection
1199  * \param user_data parameter to be passed to the queue callback
1200  *
1201  * Queue an object to be watched for collection, when the \p obj is
1202  * collected, the callback that was registered for the \p queue will
1203  * be invoked with \p user_data as argument.
1204  *
1205  * \returns FALSE if the queue is scheduled to be freed.
1206  */
1207 gboolean
mono_gc_reference_queue_add(MonoReferenceQueue * queue,MonoObject * obj,void * user_data)1208 mono_gc_reference_queue_add (MonoReferenceQueue *queue, MonoObject *obj, void *user_data)
1209 {
1210 	RefQueueEntry *entry;
1211 	if (queue->should_be_deleted)
1212 		return FALSE;
1213 
1214 	g_assert (obj != NULL);
1215 
1216 	entry = g_new0 (RefQueueEntry, 1);
1217 	entry->user_data = user_data;
1218 	entry->domain = mono_object_domain (obj);
1219 
1220 	entry->gchandle = mono_gchandle_new_weakref (obj, TRUE);
1221 	mono_object_register_finalizer (obj);
1222 
1223 	ref_list_push (&queue->queue, entry);
1224 	return TRUE;
1225 }
1226 
1227 /**
1228  * mono_gc_reference_queue_free:
1229  * \param queue the queue that should be freed.
1230  *
1231  * This operation signals that \p queue should be freed. This operation is deferred
1232  * as it happens on the finalizer thread.
1233  *
1234  * After this call, no further objects can be queued. It's the responsibility of the
1235  * caller to make sure that no further attempt to access queue will be made.
1236  */
1237 void
mono_gc_reference_queue_free(MonoReferenceQueue * queue)1238 mono_gc_reference_queue_free (MonoReferenceQueue *queue)
1239 {
1240 	queue->should_be_deleted = TRUE;
1241 }
1242