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);
258 mono_native_tls_set_value (thread_info_key, NULL);
263 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
266 threads_callbacks = *callbacks;
267 thread_info_size = info_size;
269 res = mono_native_tls_alloc (&thread_info_key, NULL);
271 res = mono_native_tls_alloc (&thread_info_key, unregister_thread);
275 res = mono_native_tls_alloc (&small_id_key, NULL);
278 mono_mutex_init_suspend_safe (&global_suspend_lock);
280 mono_lls_init (&thread_list, NULL);
281 mono_thread_smr_init ();
282 mono_threads_init_platform ();
284 mono_threads_inited = TRUE;
286 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
290 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
292 runtime_callbacks = *callbacks;
295 MonoThreadInfoCallbacks *
296 mono_threads_get_callbacks (void)
298 return &threads_callbacks;
301 MonoThreadInfoRuntimeCallbacks *
302 mono_threads_get_runtime_callbacks (void)
304 return &runtime_callbacks;
308 The return value is only valid until a matching mono_thread_info_resume is called
310 static MonoThreadInfo*
311 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
313 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
314 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
318 mono_mutex_lock (&info->suspend_lock);
320 /*thread is on the process of detaching*/
321 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
322 mono_hazard_pointer_clear (hp, 1);
326 THREADS_DEBUG ("suspend %x IN COUNT %d\n", tid, info->suspend_count);
328 if (info->suspend_count) {
329 ++info->suspend_count;
330 mono_hazard_pointer_clear (hp, 1);
331 mono_mutex_unlock (&info->suspend_lock);
335 if (!mono_threads_core_suspend (info)) {
336 mono_mutex_unlock (&info->suspend_lock);
337 mono_hazard_pointer_clear (hp, 1);
341 if (interrupt_kernel)
342 mono_threads_core_interrupt (info);
344 ++info->suspend_count;
345 info->thread_state |= STATE_SUSPENDED;
346 mono_mutex_unlock (&info->suspend_lock);
347 mono_hazard_pointer_clear (hp, 1);
353 mono_thread_info_self_suspend (void)
356 MonoThreadInfo *info = mono_thread_info_current ();
360 mono_mutex_lock (&info->suspend_lock);
362 THREADS_DEBUG ("self suspend IN COUNT %d\n", info->suspend_count);
364 g_assert (info->suspend_count == 0);
365 ++info->suspend_count;
367 info->thread_state |= STATE_SELF_SUSPENDED;
369 ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->suspend_state, NULL);
372 mono_mutex_unlock (&info->suspend_lock);
374 while (MONO_SEM_WAIT (&info->resume_semaphore) != 0) {
375 /*if (EINTR != errno) ABORT("sem_wait failed"); */
378 g_assert (!info->async_target); /*FIXME this should happen normally for suspend. */
379 MONO_SEM_POST (&info->finish_resume_semaphore);
383 mono_thread_info_resume_internal (MonoThreadInfo *info)
386 if (mono_thread_info_suspend_state (info) == STATE_SELF_SUSPENDED) {
387 MONO_SEM_POST (&info->resume_semaphore);
388 while (MONO_SEM_WAIT (&info->finish_resume_semaphore) != 0) {
389 /* g_assert (errno == EINTR); */
393 result = mono_threads_core_resume (info);
395 info->thread_state &= ~SUSPEND_STATE_MASK;
400 mono_thread_info_resume (MonoNativeThreadId tid)
402 gboolean result = TRUE;
403 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
404 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
408 mono_mutex_lock (&info->suspend_lock);
410 THREADS_DEBUG ("resume %x IN COUNT %d\n",tid, info->suspend_count);
412 if (info->suspend_count <= 0) {
413 mono_mutex_unlock (&info->suspend_lock);
414 mono_hazard_pointer_clear (hp, 1);
419 * The theory here is that if we manage to suspend the thread it means it did not
420 * start cleanup since it take the same lock.
422 g_assert (mono_thread_info_get_tid (info));
424 if (--info->suspend_count == 0)
425 result = mono_thread_info_resume_internal (info);
427 mono_mutex_unlock (&info->suspend_lock);
428 mono_hazard_pointer_clear (hp, 1);
434 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
435 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
438 is_thread_in_critical_region (MonoThreadInfo *info)
441 MonoJitInfo *ji = mono_jit_info_table_find (
442 info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN],
443 MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
450 return threads_callbacks.mono_method_is_critical (method);
455 If we are trying to suspend a target that is on a critical region
456 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
457 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
460 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
462 MonoThreadInfo *info = NULL;
463 int sleep_duration = 0;
465 /*FIXME: unify this with self-suspend*/
466 g_assert (id != mono_native_thread_id_get ());
468 mono_thread_info_suspend_lock ();
471 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel))) {
472 g_warning ("failed to suspend thread %p, hopefully it is dead", (gpointer)id);
473 mono_thread_info_suspend_unlock ();
476 /*WARNING: We now are in interrupt context until we resume the thread. */
477 if (!is_thread_in_critical_region (info))
480 if (!mono_thread_info_resume (id)) {
481 g_warning ("failed to result thread %p, hopefully it is dead", (gpointer)id);
482 mono_thread_info_suspend_unlock ();
485 THREADS_DEBUG ("restarted thread %p\n", (gpointer)id);
487 if (!sleep_duration) {
495 g_usleep (sleep_duration);
497 sleep_duration += 10;
500 mono_thread_info_suspend_unlock ();
505 Inject an assynchronous call into the target thread. The target thread must be suspended and
506 only a single async call can be setup for a given suspend cycle.
507 This async call must cause stack unwinding as the current implementation doesn't save enough state
508 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
509 currently used only to deliver exceptions.
512 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
514 g_assert (info->suspend_count);
515 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
516 g_assert (!info->async_target);
517 info->async_target = target_func;
518 /* This is not GC tracked */
519 info->user_data = user_data;
523 The suspend lock is held during any suspend in progress.
524 A GC that has safepoints must take this lock as part of its
525 STW to make sure no unsafe pending suspend is in progress.
528 mono_thread_info_suspend_lock (void)
530 mono_mutex_lock (&global_suspend_lock);
534 mono_thread_info_suspend_unlock (void)
536 mono_mutex_unlock (&global_suspend_lock);
540 mono_thread_info_disable_new_interrupt (gboolean disable)
542 disable_new_interrupt = disable;
546 * This is a very specific function whose only purpose is to
547 * break a given thread from socket syscalls.
549 * This only exists because linux won't fail a call to connect
550 * if the underlying is closed.
552 * TODO We should cleanup and unify this with the other syscall abort
556 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
558 MonoThreadHazardPointers *hp;
559 MonoThreadInfo *info;
561 if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
564 hp = mono_hazard_pointer_get ();
565 info = mono_thread_info_lookup (tid); /*info on HP1*/
569 if (mono_thread_info_run_state (info) > STATE_RUNNING) {
570 mono_hazard_pointer_clear (hp, 1);
574 mono_thread_info_suspend_lock ();
576 mono_threads_core_abort_syscall (info);
578 mono_hazard_pointer_clear (hp, 1);
579 mono_thread_info_suspend_unlock ();
583 Disabled by default for now.
584 To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
585 which means mono-context and setup_async_callback, and we need a mono-threads backend.
588 mono_thread_info_new_interrupt_enabled (void)
590 /*We need STW gc events to work correctly*/
591 #if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
595 #if defined(HOST_WIN32)
598 #if defined (__i386__)
599 return !disable_new_interrupt;