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/utils/mono-memory-model.h>
17 #include <mono/metadata/appdomain.h>
18 #include <mono/metadata/domain-internals.h>
23 #include <mono/utils/mach-support.h>
26 #define THREADS_DEBUG(...)
27 //#define THREADS_DEBUG(...) g_message(__VA_ARGS__)
30 Mutex that makes sure only a single thread can be suspending others.
31 Suspend is a very racy operation since it requires restarting until
32 the target thread is not on an unsafe region.
34 We could implement this using critical regions, but would be much much
35 harder for an operation that is hardly performance critical.
37 The GC has to acquire this lock before starting a STW to make sure
38 a runtime suspend won't make it wronly see a thread in a safepoint
39 when it is in fact not.
41 static MonoSemType global_suspend_semaphore;
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_SEM_DESTROY (&info->suspend_semaphore);
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_SEM_INIT (&info->suspend_semaphore, 1);
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_SEM_INIT (&global_suspend_semaphore, 1);
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_SEM_WAIT_UNITERRUPTIBLE (&info->suspend_semaphore);
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_SEM_POST (&info->suspend_semaphore);
343 if (!mono_threads_core_suspend (info)) {
344 MONO_SEM_POST (&info->suspend_semaphore);
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_SEM_POST (&info->suspend_semaphore);
355 mono_hazard_pointer_clear (hp, 1);
361 mono_thread_info_self_suspend (void)
364 MonoThreadInfo *info = mono_thread_info_current ();
368 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->suspend_semaphore);
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_SEM_POST (&info->suspend_semaphore);
382 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
384 g_assert (!info->async_target); /*FIXME this should happen normally for suspend. */
385 MONO_SEM_POST (&info->finish_resume_semaphore);
389 mono_thread_info_resume_internal (MonoThreadInfo *info)
392 if (mono_thread_info_suspend_state (info) == STATE_SELF_SUSPENDED) {
393 MONO_SEM_POST (&info->resume_semaphore);
394 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->finish_resume_semaphore);
397 result = mono_threads_core_resume (info);
399 info->thread_state &= ~SUSPEND_STATE_MASK;
404 mono_thread_info_resume (MonoNativeThreadId tid)
406 gboolean result = TRUE;
407 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
408 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
412 MONO_SEM_WAIT_UNITERRUPTIBLE (&info->suspend_semaphore);
414 THREADS_DEBUG ("resume %x IN COUNT %d\n",tid, info->suspend_count);
416 if (info->suspend_count <= 0) {
417 MONO_SEM_POST (&info->suspend_semaphore);
418 mono_hazard_pointer_clear (hp, 1);
423 * The theory here is that if we manage to suspend the thread it means it did not
424 * start cleanup since it take the same lock.
426 g_assert (mono_thread_info_get_tid (info));
428 if (--info->suspend_count == 0)
429 result = mono_thread_info_resume_internal (info);
431 MONO_SEM_POST (&info->suspend_semaphore);
432 mono_hazard_pointer_clear (hp, 1);
433 mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, FALSE);
439 mono_thread_info_finish_suspend (void)
441 mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, FALSE);
445 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
446 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
449 is_thread_in_critical_region (MonoThreadInfo *info)
454 if (info->inside_critical_region)
457 ji = mono_jit_info_table_find (
458 info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN],
459 MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
466 return threads_callbacks.mono_method_is_critical (method);
471 If we are trying to suspend a target that is on a critical region
472 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
473 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
476 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
478 MonoThreadInfo *info = NULL;
479 int sleep_duration = 0;
481 /*FIXME: unify this with self-suspend*/
482 g_assert (id != mono_native_thread_id_get ());
484 mono_thread_info_suspend_lock ();
487 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel))) {
488 g_warning ("failed to suspend thread %p, hopefully it is dead", (gpointer)id);
489 mono_thread_info_suspend_unlock ();
492 /*WARNING: We now are in interrupt context until we resume the thread. */
493 if (!is_thread_in_critical_region (info))
496 if (!mono_thread_info_resume (id)) {
497 g_warning ("failed to result thread %p, hopefully it is dead", (gpointer)id);
498 mono_thread_info_suspend_unlock ();
501 THREADS_DEBUG ("restarted thread %p\n", (gpointer)id);
503 if (!sleep_duration) {
511 g_usleep (sleep_duration);
513 sleep_duration += 10;
516 mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE);
518 mono_thread_info_suspend_unlock ();
523 Inject an assynchronous call into the target thread. The target thread must be suspended and
524 only a single async call can be setup for a given suspend cycle.
525 This async call must cause stack unwinding as the current implementation doesn't save enough state
526 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
527 currently used only to deliver exceptions.
530 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
532 g_assert (info->suspend_count);
533 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
534 g_assert (!info->async_target);
535 info->async_target = target_func;
536 /* This is not GC tracked */
537 info->user_data = user_data;
541 The suspend lock is held during any suspend in progress.
542 A GC that has safepoints must take this lock as part of its
543 STW to make sure no unsafe pending suspend is in progress.
546 mono_thread_info_suspend_lock (void)
548 MONO_SEM_WAIT_UNITERRUPTIBLE (&global_suspend_semaphore);
552 mono_thread_info_suspend_unlock (void)
554 MONO_SEM_POST (&global_suspend_semaphore);
558 mono_thread_info_disable_new_interrupt (gboolean disable)
560 disable_new_interrupt = disable;
564 * This is a very specific function whose only purpose is to
565 * break a given thread from socket syscalls.
567 * This only exists because linux won't fail a call to connect
568 * if the underlying is closed.
570 * TODO We should cleanup and unify this with the other syscall abort
574 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
576 MonoThreadHazardPointers *hp;
577 MonoThreadInfo *info;
579 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
582 hp = mono_hazard_pointer_get ();
583 info = mono_thread_info_lookup (tid); /*info on HP1*/
587 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
588 mono_hazard_pointer_clear (hp, 1);
592 mono_thread_info_suspend_lock ();
594 mono_threads_core_abort_syscall (info);
596 mono_hazard_pointer_clear (hp, 1);
597 mono_thread_info_suspend_unlock ();
601 Disabled by default for now.
602 To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
603 which means mono-context and setup_async_callback, and we need a mono-threads backend.
606 mono_thread_info_new_interrupt_enabled (void)
608 /*We need STW gc events to work correctly*/
609 #if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
613 #if defined(HOST_WIN32)
616 #if defined (__i386__)
617 return !disable_new_interrupt;