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