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>
21 #define THREADS_DEBUG(...)
22 //#define THREADS_DEBUG(...) g_message(__VA_ARGS__)
25 Mutex that makes sure only a single thread can be suspending others.
26 Suspend is a very racy operation since it requires restarting until
27 the target thread is not on an unsafe region.
29 We could implement this using critical regions, but would be much much
30 harder for an operation that is hardly performance critical.
32 The GC has to acquire this lock before starting a STW to make sure
33 a runtime suspend won't make it wronly see a thread in a safepoint
34 when it is in fact not.
36 static mono_mutex_t global_suspend_lock;
39 static int thread_info_size;
40 static MonoThreadInfoCallbacks threads_callbacks;
41 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
42 static MonoNativeTlsKey thread_info_key, small_id_key;
43 static MonoLinkedListSet thread_list;
44 static gboolean disable_new_interrupt = FALSE;
45 static gboolean mono_threads_inited = FALSE;
48 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
51 mono_hazard_pointer_clear (hp, 0);
53 mono_hazard_pointer_clear (hp, 1);
55 mono_hazard_pointer_clear (hp, 2);
59 If return non null Hazard Pointer 1 holds the return value.
62 mono_thread_info_lookup (MonoNativeThreadId id)
64 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
66 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
67 mono_hazard_pointer_clear_all (hp, -1);
71 mono_hazard_pointer_clear_all (hp, 1);
72 return mono_hazard_pointer_get_val (hp, 1);
76 mono_thread_info_insert (MonoThreadInfo *info)
78 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
80 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
81 mono_hazard_pointer_clear_all (hp, -1);
85 mono_hazard_pointer_clear_all (hp, -1);
90 mono_thread_info_remove (MonoThreadInfo *info)
92 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
95 THREADS_DEBUG ("removing info %p\n", info);
96 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
97 mono_hazard_pointer_clear_all (hp, -1);
102 free_thread_info (gpointer mem)
104 MonoThreadInfo *info = mem;
106 mono_mutex_destroy (&info->suspend_lock);
107 MONO_SEM_DESTROY (&info->resume_semaphore);
108 MONO_SEM_DESTROY (&info->finish_resume_semaphore);
109 mono_threads_platform_free (info);
115 mono_thread_info_register_small_id (void)
117 int small_id = mono_thread_small_id_alloc ();
118 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
123 register_thread (MonoThreadInfo *info, gpointer baseptr)
125 int small_id = mono_thread_info_register_small_id ();
127 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
128 info->small_id = small_id;
130 mono_mutex_init_suspend_safe (&info->suspend_lock);
131 MONO_SEM_INIT (&info->resume_semaphore, 0);
132 MONO_SEM_INIT (&info->finish_resume_semaphore, 0);
134 /*set TLS early so SMR works */
135 mono_native_tls_set_value (thread_info_key, info);
137 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
139 if (threads_callbacks.thread_register) {
140 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
141 g_warning ("thread registation failed\n");
147 mono_threads_platform_register (info);
149 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
150 result = mono_thread_info_insert (info);
156 unregister_thread (void *arg)
158 MonoThreadInfo *info = arg;
159 int small_id = info->small_id;
162 THREADS_DEBUG ("unregistering info %p\n", info);
165 * TLS destruction order is not reliable so small_id might be cleaned up
168 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
171 The unregister callback is reposible for calling mono_threads_unregister_current_thread
172 since it usually needs to be done in sync with the GC does a stop-the-world.
174 if (threads_callbacks.thread_unregister)
175 threads_callbacks.thread_unregister (info);
177 mono_threads_unregister_current_thread (info);
179 /*now it's safe to free the thread info.*/
180 mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
181 mono_thread_small_id_free (small_id);
185 * Removes the current thread from the thread list.
186 * This must be called from the thread unregister callback and nowhere else.
187 * The current thread must be passed as TLS might have already been cleaned up.
190 mono_threads_unregister_current_thread (MonoThreadInfo *info)
193 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
194 result = mono_thread_info_remove (info);
200 mono_thread_info_current (void)
202 return mono_native_tls_get_value (thread_info_key);
206 mono_thread_info_get_small_id (void)
208 gpointer val = mono_native_tls_get_value (small_id_key);
211 return GPOINTER_TO_INT (val) - 1;
215 mono_thread_info_list_head (void)
221 mono_thread_info_attach (void *baseptr)
223 MonoThreadInfo *info;
224 if (!mono_threads_inited)
226 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
227 * thread is created before an embedding API user initialized Mono. */
228 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
231 info = mono_native_tls_get_value (thread_info_key);
233 info = g_malloc0 (thread_info_size);
234 THREADS_DEBUG ("attaching %p\n", info);
235 if (!register_thread (info, baseptr))
237 } else if (threads_callbacks.thread_attach) {
238 threads_callbacks.thread_attach (info);
244 mono_thread_info_dettach (void)
246 MonoThreadInfo *info;
247 if (!mono_threads_inited)
249 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
250 * is created before an embedding API user initialized Mono. */
251 THREADS_DEBUG ("mono_thread_info_dettach called before mono_threads_init\n");
254 info = mono_native_tls_get_value (thread_info_key);
256 THREADS_DEBUG ("detaching %p\n", info);
257 unregister_thread (info);
262 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
265 threads_callbacks = *callbacks;
266 thread_info_size = info_size;
268 res = mono_native_tls_alloc (&thread_info_key, NULL);
270 res = mono_native_tls_alloc (&thread_info_key, unregister_thread);
274 res = mono_native_tls_alloc (&small_id_key, NULL);
277 mono_mutex_init_suspend_safe (&global_suspend_lock);
279 mono_lls_init (&thread_list, NULL);
280 mono_thread_smr_init ();
281 mono_threads_init_platform ();
283 mono_threads_inited = TRUE;
285 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
289 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
291 runtime_callbacks = *callbacks;
294 MonoThreadInfoCallbacks *
295 mono_threads_get_callbacks (void)
297 return &threads_callbacks;
300 MonoThreadInfoRuntimeCallbacks *
301 mono_threads_get_runtime_callbacks (void)
303 return &runtime_callbacks;
307 The return value is only valid until a matching mono_thread_info_resume is called
309 static MonoThreadInfo*
310 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
312 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
313 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
317 mono_mutex_lock (&info->suspend_lock);
319 /*thread is on the process of detaching*/
320 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
321 mono_hazard_pointer_clear (hp, 1);
325 THREADS_DEBUG ("suspend %x IN COUNT %d\n", tid, info->suspend_count);
327 if (info->suspend_count) {
328 ++info->suspend_count;
329 mono_hazard_pointer_clear (hp, 1);
330 mono_mutex_unlock (&info->suspend_lock);
334 if (!mono_threads_core_suspend (info)) {
335 mono_mutex_unlock (&info->suspend_lock);
336 mono_hazard_pointer_clear (hp, 1);
340 if (interrupt_kernel)
341 mono_threads_core_interrupt (info);
343 ++info->suspend_count;
344 info->thread_state |= STATE_SUSPENDED;
345 mono_mutex_unlock (&info->suspend_lock);
346 mono_hazard_pointer_clear (hp, 1);
352 mono_thread_info_self_suspend (void)
355 MonoThreadInfo *info = mono_thread_info_current ();
359 mono_mutex_lock (&info->suspend_lock);
361 THREADS_DEBUG ("self suspend IN COUNT %d\n", info->suspend_count);
363 g_assert (info->suspend_count == 0);
364 ++info->suspend_count;
366 info->thread_state |= STATE_SELF_SUSPENDED;
368 ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->suspend_state, NULL);
371 mono_mutex_unlock (&info->suspend_lock);
373 while (MONO_SEM_WAIT (&info->resume_semaphore) != 0) {
374 /*if (EINTR != errno) ABORT("sem_wait failed"); */
377 g_assert (!info->async_target); /*FIXME this should happen normally for suspend. */
378 MONO_SEM_POST (&info->finish_resume_semaphore);
382 mono_thread_info_resume_internal (MonoThreadInfo *info)
385 if (mono_thread_info_suspend_state (info) == STATE_SELF_SUSPENDED) {
386 MONO_SEM_POST (&info->resume_semaphore);
387 while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
388 /* g_assert (errno == EINTR); */
392 result = mono_threads_core_resume (info);
394 info->thread_state &= ~SUSPEND_STATE_MASK;
399 mono_thread_info_resume (MonoNativeThreadId tid)
401 gboolean result = TRUE;
402 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
403 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
407 mono_mutex_lock (&info->suspend_lock);
409 THREADS_DEBUG ("resume %x IN COUNT %d\n",tid, info->suspend_count);
411 if (info->suspend_count <= 0) {
412 mono_mutex_unlock (&info->suspend_lock);
413 mono_hazard_pointer_clear (hp, 1);
418 * The theory here is that if we manage to suspend the thread it means it did not
419 * start cleanup since it take the same lock.
421 g_assert (mono_thread_info_get_tid (info));
423 if (--info->suspend_count == 0)
424 result = mono_thread_info_resume_internal (info);
426 mono_mutex_unlock (&info->suspend_lock);
427 mono_hazard_pointer_clear (hp, 1);
433 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
434 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
437 is_thread_in_critical_region (MonoThreadInfo *info)
440 MonoJitInfo *ji = mono_jit_info_table_find (
441 info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN],
442 MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
449 return threads_callbacks.mono_method_is_critical (method);
454 If we are trying to suspend a target that is on a critical region
455 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
456 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
459 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
461 MonoThreadInfo *info = NULL;
462 int sleep_duration = 0;
464 /*FIXME: unify this with self-suspend*/
465 g_assert (id != mono_native_thread_id_get ());
467 mono_thread_info_suspend_lock ();
470 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel))) {
471 g_warning ("failed to suspend thread %p, hopefully it is dead", (gpointer)id);
472 mono_thread_info_suspend_unlock ();
475 /*WARNING: We now are in interrupt context until we resume the thread. */
476 if (!is_thread_in_critical_region (info))
479 if (!mono_thread_info_resume (id)) {
480 g_warning ("failed to result thread %p, hopefully it is dead", (gpointer)id);
481 mono_thread_info_suspend_unlock ();
484 THREADS_DEBUG ("restarted thread %p\n", (gpointer)id);
486 if (!sleep_duration) {
494 g_usleep (sleep_duration);
496 sleep_duration += 10;
499 mono_thread_info_suspend_unlock ();
504 Inject an assynchronous call into the target thread. The target thread must be suspended and
505 only a single async call can be setup for a given suspend cycle.
506 This async call must cause stack unwinding as the current implementation doesn't save enough state
507 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
508 currently used only to deliver exceptions.
511 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
513 g_assert (info->suspend_count);
514 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
515 g_assert (!info->async_target);
516 info->async_target = target_func;
517 /* This is not GC tracked */
518 info->user_data = user_data;
522 The suspend lock is held during any suspend in progress.
523 A GC that has safepoints must take this lock as part of its
524 STW to make sure no unsafe pending suspend is in progress.
527 mono_thread_info_suspend_lock (void)
529 mono_mutex_lock (&global_suspend_lock);
533 mono_thread_info_suspend_unlock (void)
535 mono_mutex_unlock (&global_suspend_lock);
539 mono_thread_info_disable_new_interrupt (gboolean disable)
541 disable_new_interrupt = disable;
545 * This is a very specific function whose only purpose is to
546 * break a given thread from socket syscalls.
548 * This only exists because linux won't fail a call to connect
549 * if the underlying is closed.
551 * TODO We should cleanup and unify this with the other syscall abort
555 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
557 MonoThreadHazardPointers *hp;
558 MonoThreadInfo *info;
560 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
563 hp = mono_hazard_pointer_get ();
564 info = mono_thread_info_lookup (tid); /*info on HP1*/
568 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
569 mono_hazard_pointer_clear (hp, 1);
573 mono_thread_info_suspend_lock ();
575 mono_threads_core_abort_syscall (info);
577 mono_hazard_pointer_clear (hp, 1);
578 mono_thread_info_suspend_unlock ();
582 Disabled by default for now.
583 To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
584 which means mono-context and setup_async_callback, and we need a mono-threads backend.
587 mono_thread_info_new_interrupt_enabled (void)
589 /*We need STW gc events to work correctly*/
590 #if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
594 #if defined(HOST_WIN32)
597 #if defined (__i386__)
598 return !disable_new_interrupt;