2 * mono-threads.c: Low-level threading
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * Copyright 2011 Novell, Inc (http://www.novell.com)
8 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
11 #include <mono/utils/mono-compiler.h>
12 #include <mono/utils/mono-semaphore.h>
13 #include <mono/utils/mono-threads.h>
14 #include <mono/utils/mono-tls.h>
15 #include <mono/utils/hazard-pointer.h>
16 #include <mono/metadata/appdomain.h>
17 #include <mono/metadata/domain-internals.h>
22 #include <mono/utils/mach-support.h>
25 #define THREADS_DEBUG(...)
26 //#define THREADS_DEBUG(...) g_message(__VA_ARGS__)
29 Mutex that makes sure only a single thread can be suspending others.
30 Suspend is a very racy operation since it requires restarting until
31 the target thread is not on an unsafe region.
33 We could implement this using critical regions, but would be much much
34 harder for an operation that is hardly performance critical.
36 The GC has to acquire this lock before starting a STW to make sure
37 a runtime suspend won't make it wronly see a thread in a safepoint
38 when it is in fact not.
40 static mono_mutex_t global_suspend_lock;
43 static int thread_info_size;
44 static MonoThreadInfoCallbacks threads_callbacks;
45 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
46 static MonoNativeTlsKey thread_info_key, small_id_key;
47 static MonoLinkedListSet thread_list;
48 static gboolean disable_new_interrupt = FALSE;
49 static gboolean mono_threads_inited = FALSE;
52 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
55 mono_hazard_pointer_clear (hp, 0);
57 mono_hazard_pointer_clear (hp, 1);
59 mono_hazard_pointer_clear (hp, 2);
63 If return non null Hazard Pointer 1 holds the return value.
66 mono_thread_info_lookup (MonoNativeThreadId id)
68 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
70 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
71 mono_hazard_pointer_clear_all (hp, -1);
75 mono_hazard_pointer_clear_all (hp, 1);
76 return mono_hazard_pointer_get_val (hp, 1);
80 mono_thread_info_insert (MonoThreadInfo *info)
82 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
84 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
85 mono_hazard_pointer_clear_all (hp, -1);
89 mono_hazard_pointer_clear_all (hp, -1);
94 mono_thread_info_remove (MonoThreadInfo *info)
96 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
99 THREADS_DEBUG ("removing info %p\n", info);
100 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
101 mono_hazard_pointer_clear_all (hp, -1);
106 free_thread_info (gpointer mem)
108 MonoThreadInfo *info = mem;
110 mono_mutex_destroy (&info->suspend_lock);
111 MONO_SEM_DESTROY (&info->resume_semaphore);
112 MONO_SEM_DESTROY (&info->finish_resume_semaphore);
113 mono_threads_platform_free (info);
119 mono_thread_info_register_small_id (void)
121 int small_id = mono_thread_small_id_alloc ();
122 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
127 register_thread (MonoThreadInfo *info, gpointer baseptr)
129 int small_id = mono_thread_info_register_small_id ();
131 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
132 info->small_id = small_id;
134 mono_mutex_init_suspend_safe (&info->suspend_lock);
135 MONO_SEM_INIT (&info->resume_semaphore, 0);
136 MONO_SEM_INIT (&info->finish_resume_semaphore, 0);
138 /*set TLS early so SMR works */
139 mono_native_tls_set_value (thread_info_key, info);
141 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
143 if (threads_callbacks.thread_register) {
144 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
145 g_warning ("thread registation failed\n");
151 mono_threads_platform_register (info);
153 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
154 result = mono_thread_info_insert (info);
160 unregister_thread (void *arg)
162 MonoThreadInfo *info = arg;
163 int small_id = info->small_id;
166 THREADS_DEBUG ("unregistering info %p\n", info);
169 * TLS destruction order is not reliable so small_id might be cleaned up
172 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
175 The unregister callback is reposible for calling mono_threads_unregister_current_thread
176 since it usually needs to be done in sync with the GC does a stop-the-world.
178 if (threads_callbacks.thread_unregister)
179 threads_callbacks.thread_unregister (info);
181 mono_threads_unregister_current_thread (info);
183 /*now it's safe to free the thread info.*/
184 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
185 mono_thread_small_id_free (small_id);
189 * Removes the current thread from the thread list.
190 * This must be called from the thread unregister callback and nowhere else.
191 * The current thread must be passed as TLS might have already been cleaned up.
194 mono_threads_unregister_current_thread (MonoThreadInfo *info)
197 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
198 result = mono_thread_info_remove (info);
204 mono_thread_info_current (void)
206 return mono_native_tls_get_value (thread_info_key);
210 mono_thread_info_get_small_id (void)
212 gpointer val = mono_native_tls_get_value (small_id_key);
215 return GPOINTER_TO_INT (val) - 1;
219 mono_thread_info_list_head (void)
225 mono_thread_info_attach (void *baseptr)
227 MonoThreadInfo *info;
228 if (!mono_threads_inited)
230 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
231 * thread is created before an embedding API user initialized Mono. */
232 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
235 info = mono_native_tls_get_value (thread_info_key);
237 info = g_malloc0 (thread_info_size);
238 THREADS_DEBUG ("attaching %p\n", info);
239 if (!register_thread (info, baseptr))
241 } else if (threads_callbacks.thread_attach) {
242 threads_callbacks.thread_attach (info);
248 mono_thread_info_dettach (void)
250 MonoThreadInfo *info;
251 if (!mono_threads_inited)
253 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
254 * is created before an embedding API user initialized Mono. */
255 THREADS_DEBUG ("mono_thread_info_dettach called before mono_threads_init\n");
258 info = mono_native_tls_get_value (thread_info_key);
260 THREADS_DEBUG ("detaching %p\n", info);
261 unregister_thread (info);
262 mono_native_tls_set_value (thread_info_key, NULL);
267 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
270 threads_callbacks = *callbacks;
271 thread_info_size = info_size;
273 res = mono_native_tls_alloc (&thread_info_key, NULL);
275 res = mono_native_tls_alloc (&thread_info_key, unregister_thread);
279 res = mono_native_tls_alloc (&small_id_key, NULL);
282 mono_mutex_init_suspend_safe (&global_suspend_lock);
284 mono_lls_init (&thread_list, NULL);
285 mono_thread_smr_init ();
286 mono_threads_init_platform ();
288 #if defined(__MACH__)
289 mono_mach_init (thread_info_key);
292 mono_threads_inited = TRUE;
294 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
298 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
300 runtime_callbacks = *callbacks;
303 MonoThreadInfoCallbacks *
304 mono_threads_get_callbacks (void)
306 return &threads_callbacks;
309 MonoThreadInfoRuntimeCallbacks *
310 mono_threads_get_runtime_callbacks (void)
312 return &runtime_callbacks;
316 The return value is only valid until a matching mono_thread_info_resume is called
318 static MonoThreadInfo*
319 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
321 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
322 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
326 mono_mutex_lock (&info->suspend_lock);
328 /*thread is on the process of detaching*/
329 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
330 mono_hazard_pointer_clear (hp, 1);
334 THREADS_DEBUG ("suspend %x IN COUNT %d\n", tid, info->suspend_count);
336 if (info->suspend_count) {
337 ++info->suspend_count;
338 mono_hazard_pointer_clear (hp, 1);
339 mono_mutex_unlock (&info->suspend_lock);
343 if (!mono_threads_core_suspend (info)) {
344 mono_mutex_unlock (&info->suspend_lock);
345 mono_hazard_pointer_clear (hp, 1);
349 if (interrupt_kernel)
350 mono_threads_core_interrupt (info);
352 ++info->suspend_count;
353 info->thread_state |= STATE_SUSPENDED;
354 mono_mutex_unlock (&info->suspend_lock);
355 mono_hazard_pointer_clear (hp, 1);
361 mono_thread_info_self_suspend (void)
364 MonoThreadInfo *info = mono_thread_info_current ();
368 mono_mutex_lock (&info->suspend_lock);
370 THREADS_DEBUG ("self suspend IN COUNT %d\n", info->suspend_count);
372 g_assert (info->suspend_count == 0);
373 ++info->suspend_count;
375 info->thread_state |= STATE_SELF_SUSPENDED;
377 ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->suspend_state, NULL);
380 mono_mutex_unlock (&info->suspend_lock);
382 while (MONO_SEM_WAIT (&info->resume_semaphore) != 0) {
383 /*if (EINTR != errno) ABORT("sem_wait failed"); */
386 g_assert (!info->async_target); /*FIXME this should happen normally for suspend. */
387 MONO_SEM_POST (&info->finish_resume_semaphore);
391 mono_thread_info_resume_internal (MonoThreadInfo *info)
394 if (mono_thread_info_suspend_state (info) == STATE_SELF_SUSPENDED) {
395 MONO_SEM_POST (&info->resume_semaphore);
396 while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
397 /* g_assert (errno == EINTR); */
401 result = mono_threads_core_resume (info);
403 info->thread_state &= ~SUSPEND_STATE_MASK;
408 mono_thread_info_resume (MonoNativeThreadId tid)
410 gboolean result = TRUE;
411 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
412 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
416 mono_mutex_lock (&info->suspend_lock);
418 THREADS_DEBUG ("resume %x IN COUNT %d\n",tid, info->suspend_count);
420 if (info->suspend_count <= 0) {
421 mono_mutex_unlock (&info->suspend_lock);
422 mono_hazard_pointer_clear (hp, 1);
427 * The theory here is that if we manage to suspend the thread it means it did not
428 * start cleanup since it take the same lock.
430 g_assert (mono_thread_info_get_tid (info));
432 if (--info->suspend_count == 0)
433 result = mono_thread_info_resume_internal (info);
435 mono_mutex_unlock (&info->suspend_lock);
436 mono_hazard_pointer_clear (hp, 1);
442 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
443 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
446 is_thread_in_critical_region (MonoThreadInfo *info)
449 MonoJitInfo *ji = mono_jit_info_table_find (
450 info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN],
451 MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
458 return threads_callbacks.mono_method_is_critical (method);
463 If we are trying to suspend a target that is on a critical region
464 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
465 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
468 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
470 MonoThreadInfo *info = NULL;
471 int sleep_duration = 0;
473 /*FIXME: unify this with self-suspend*/
474 g_assert (id != mono_native_thread_id_get ());
476 mono_thread_info_suspend_lock ();
479 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel))) {
480 g_warning ("failed to suspend thread %p, hopefully it is dead", (gpointer)id);
481 mono_thread_info_suspend_unlock ();
484 /*WARNING: We now are in interrupt context until we resume the thread. */
485 if (!is_thread_in_critical_region (info))
488 if (!mono_thread_info_resume (id)) {
489 g_warning ("failed to result thread %p, hopefully it is dead", (gpointer)id);
490 mono_thread_info_suspend_unlock ();
493 THREADS_DEBUG ("restarted thread %p\n", (gpointer)id);
495 if (!sleep_duration) {
503 g_usleep (sleep_duration);
505 sleep_duration += 10;
508 mono_thread_info_suspend_unlock ();
513 Inject an assynchronous call into the target thread. The target thread must be suspended and
514 only a single async call can be setup for a given suspend cycle.
515 This async call must cause stack unwinding as the current implementation doesn't save enough state
516 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
517 currently used only to deliver exceptions.
520 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
522 g_assert (info->suspend_count);
523 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
524 g_assert (!info->async_target);
525 info->async_target = target_func;
526 /* This is not GC tracked */
527 info->user_data = user_data;
531 The suspend lock is held during any suspend in progress.
532 A GC that has safepoints must take this lock as part of its
533 STW to make sure no unsafe pending suspend is in progress.
536 mono_thread_info_suspend_lock (void)
538 mono_mutex_lock (&global_suspend_lock);
542 mono_thread_info_suspend_unlock (void)
544 mono_mutex_unlock (&global_suspend_lock);
548 mono_thread_info_disable_new_interrupt (gboolean disable)
550 disable_new_interrupt = disable;
554 * This is a very specific function whose only purpose is to
555 * break a given thread from socket syscalls.
557 * This only exists because linux won't fail a call to connect
558 * if the underlying is closed.
560 * TODO We should cleanup and unify this with the other syscall abort
564 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
566 MonoThreadHazardPointers *hp;
567 MonoThreadInfo *info;
569 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
572 hp = mono_hazard_pointer_get ();
573 info = mono_thread_info_lookup (tid); /*info on HP1*/
577 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
578 mono_hazard_pointer_clear (hp, 1);
582 mono_thread_info_suspend_lock ();
584 mono_threads_core_abort_syscall (info);
586 mono_hazard_pointer_clear (hp, 1);
587 mono_thread_info_suspend_unlock ();
591 Disabled by default for now.
592 To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
593 which means mono-context and setup_async_callback, and we need a mono-threads backend.
596 mono_thread_info_new_interrupt_enabled (void)
598 /*We need STW gc events to work correctly*/
599 #if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
603 #if defined(HOST_WIN32)
606 #if defined (__i386__)
607 return !disable_new_interrupt;