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