2 * mono-threads.c: Low-level threading
5 * Rodrigo Kumpera (kumpera@gmail.com)
10 #include <mono/utils/mono-compiler.h>
11 #include <mono/utils/mono-semaphore.h>
12 #include <mono/utils/mono-threads.h>
13 #include <mono/utils/mono-tls.h>
14 #include <mono/utils/hazard-pointer.h>
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/domain-internals.h>
20 #define THREADS_DEBUG(...)
21 //#define THREADS_DEBUG(...) g_message(__VA_ARGS__)
24 Mutex that makes sure only a single thread can be suspending others.
25 Suspend is a very racy operation since it requires restarting until
26 the target thread is not on an unsafe region.
28 We could implement this using critical regions, but would be much much
29 harder for an operation that is hardly performance critical.
31 The GC has to acquire this lock before starting a STW to make sure
32 a runtime suspend won't make it wronly see a thread in a safepoint
33 when it is in fact not.
35 static CRITICAL_SECTION global_suspend_lock;
38 static int thread_info_size;
39 static MonoThreadInfoCallbacks threads_callbacks;
40 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
41 static MonoNativeTlsKey thread_info_key, small_id_key;
42 static MonoLinkedListSet thread_list;
43 static gboolean disable_new_interrupt = FALSE;
46 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
49 mono_hazard_pointer_clear (hp, 0);
51 mono_hazard_pointer_clear (hp, 1);
53 mono_hazard_pointer_clear (hp, 2);
57 If return non null Hazard Pointer 1 holds the return value.
60 mono_thread_info_lookup (MonoNativeThreadId id)
62 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
64 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
65 mono_hazard_pointer_clear_all (hp, -1);
69 mono_hazard_pointer_clear_all (hp, 1);
70 return mono_hazard_pointer_get_val (hp, 1);
74 mono_thread_info_insert (MonoThreadInfo *info)
76 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
78 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
79 mono_hazard_pointer_clear_all (hp, -1);
83 mono_hazard_pointer_clear_all (hp, -1);
88 mono_thread_info_remove (MonoThreadInfo *info)
90 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
93 THREADS_DEBUG ("removing info %p\n", info);
94 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
95 mono_hazard_pointer_clear_all (hp, -1);
100 free_thread_info (gpointer mem)
102 MonoThreadInfo *info = mem;
104 DeleteCriticalSection (&info->suspend_lock);
105 mono_threads_platform_free (info);
111 mono_thread_info_register_small_id (void)
113 int small_id = mono_thread_small_id_alloc ();
114 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
119 register_thread (MonoThreadInfo *info, gpointer baseptr)
121 int small_id = mono_thread_info_register_small_id ();
123 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
124 info->small_id = small_id;
126 InitializeCriticalSection (&info->suspend_lock);
128 /*set TLS early so SMR works */
129 mono_native_tls_set_value (thread_info_key, info);
131 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
133 if (threads_callbacks.thread_register) {
134 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
135 g_warning ("thread registation failed\n");
141 mono_threads_platform_register (info);
143 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
144 result = mono_thread_info_insert (info);
150 unregister_thread (void *arg)
152 MonoThreadInfo *info = arg;
153 int small_id = info->small_id;
156 THREADS_DEBUG ("unregistering info %p\n", info);
159 * TLS destruction order is not reliable so small_id might be cleaned up
162 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
165 The unregister callback is reposible for calling mono_threads_unregister_current_thread
166 since it usually needs to be done in sync with the GC does a stop-the-world.
168 if (threads_callbacks.thread_unregister)
169 threads_callbacks.thread_unregister (info);
171 mono_threads_unregister_current_thread (info);
173 /*now it's safe to free the thread info.*/
174 mono_thread_hazardous_free_or_queue (info, free_thread_info);
175 mono_thread_small_id_free (small_id);
179 * Removes the current thread from the thread list.
180 * This must be called from the thread unregister callback and nowhere else.
181 * The current thread must be passed as TLS might have already been cleaned up.
184 mono_threads_unregister_current_thread (MonoThreadInfo *info)
187 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
188 result = mono_thread_info_remove (info);
194 mono_thread_info_current (void)
196 return mono_native_tls_get_value (thread_info_key);
200 mono_thread_info_get_small_id (void)
202 gpointer val = mono_native_tls_get_value (small_id_key);
205 return GPOINTER_TO_INT (val) - 1;
209 mono_thread_info_list_head (void)
215 mono_thread_info_attach (void *baseptr)
217 MonoThreadInfo *info = mono_native_tls_get_value (thread_info_key);
219 info = g_malloc0 (thread_info_size);
220 THREADS_DEBUG ("attaching %p\n", info);
221 if (!register_thread (info, baseptr))
223 } else if (threads_callbacks.thread_attach) {
224 threads_callbacks.thread_attach (info);
230 mono_thread_info_dettach (void)
232 MonoThreadInfo *info = mono_native_tls_get_value (thread_info_key);
234 THREADS_DEBUG ("detaching %p\n", info);
235 unregister_thread (info);
240 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
243 threads_callbacks = *callbacks;
244 thread_info_size = info_size;
246 res = mono_native_tls_alloc (thread_info_key, NULL);
248 res = mono_native_tls_alloc (thread_info_key, unregister_thread);
252 res = mono_native_tls_alloc (small_id_key, NULL);
255 InitializeCriticalSection (&global_suspend_lock);
257 mono_lls_init (&thread_list, NULL);
258 mono_thread_smr_init ();
259 mono_threads_init_platform ();
261 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
265 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
267 runtime_callbacks = *callbacks;
270 MonoThreadInfoCallbacks *
271 mono_threads_get_callbacks (void)
273 return &threads_callbacks;
276 MonoThreadInfoRuntimeCallbacks *
277 mono_threads_get_runtime_callbacks (void)
279 return &runtime_callbacks;
283 The return value is only valid until a matching mono_thread_info_resume is called
285 static MonoThreadInfo*
286 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
288 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
289 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
293 EnterCriticalSection (&info->suspend_lock);
295 /*thread is on the process of detaching*/
296 if (info->thread_state > STATE_RUNNING) {
297 mono_hazard_pointer_clear (hp, 1);
301 THREADS_DEBUG ("suspend %x IN COUNT %d\n", tid, info->suspend_count);
303 if (info->suspend_count) {
304 ++info->suspend_count;
305 mono_hazard_pointer_clear (hp, 1);
306 LeaveCriticalSection (&info->suspend_lock);
310 if (!mono_threads_core_suspend (info)) {
311 LeaveCriticalSection (&info->suspend_lock);
312 mono_hazard_pointer_clear (hp, 1);
316 if (interrupt_kernel)
317 mono_threads_core_interrupt (info);
319 ++info->suspend_count;
320 LeaveCriticalSection (&info->suspend_lock);
321 mono_hazard_pointer_clear (hp, 1);
327 mono_thread_info_self_suspend ()
329 MonoThreadInfo *info = mono_thread_info_current ();
333 EnterCriticalSection (&info->suspend_lock);
335 THREADS_DEBUG ("self suspend IN COUNT %d\n", info->suspend_count);
337 g_assert (info->suspend_count == 0);
338 ++info->suspend_count;
341 The internal API contract with this function is a bit out of the ordinary.
342 mono_threads_core_self_suspend executes with suspend_lock taken and must
343 release it after capturing the current context.
345 mono_threads_core_self_suspend (info);
349 mono_thread_info_resume (MonoNativeThreadId tid)
351 gboolean result = TRUE;
352 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
353 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
357 EnterCriticalSection (&info->suspend_lock);
359 THREADS_DEBUG ("resume %x IN COUNT %d\n",tid, info->suspend_count);
361 if (info->suspend_count <= 0) {
362 LeaveCriticalSection (&info->suspend_lock);
363 mono_hazard_pointer_clear (hp, 1);
368 * The theory here is that if we manage to suspend the thread it means it did not
369 * start cleanup since it take the same lock.
371 g_assert (mono_thread_info_get_tid (info));
373 if (--info->suspend_count == 0)
374 result = mono_threads_core_resume (info);
376 LeaveCriticalSection (&info->suspend_lock);
377 mono_hazard_pointer_clear (hp, 1);
383 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
384 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
387 is_thread_in_critical_region (MonoThreadInfo *info)
390 MonoJitInfo *ji = mono_jit_info_table_find (
391 info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN],
392 MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
399 return threads_callbacks.mono_method_is_critical (method);
404 If we are trying to suspend a target that is on a critical region
405 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
406 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
409 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
411 MonoThreadInfo *info = NULL;
412 int sleep_duration = 0;
414 /*FIXME: unify this with self-suspend*/
415 g_assert (id != mono_native_thread_id_get ());
417 mono_thread_info_suspend_lock ();
420 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel))) {
421 g_warning ("failed to suspend thread %p, hopefully it is dead", (gpointer)id);
422 mono_thread_info_suspend_unlock ();
425 /*WARNING: We now are in interrupt context until we resume the thread. */
426 if (!is_thread_in_critical_region (info))
429 if (!mono_thread_info_resume (id)) {
430 g_warning ("failed to result thread %p, hopefully it is dead", (gpointer)id);
431 mono_thread_info_suspend_unlock ();
434 THREADS_DEBUG ("restarted thread %p\n", (gpointer)id);
436 if (!sleep_duration) {
444 g_usleep (sleep_duration);
446 sleep_duration += 10;
449 mono_thread_info_suspend_unlock ();
454 Inject an assynchronous call into the target thread. The target thread must be suspended and
455 only a single async call can be setup for a given suspend cycle.
456 This async call must cause stack unwinding as the current implementation doesn't save enough state
457 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
458 currently used only to deliver exceptions.
461 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
463 g_assert (info->suspend_count);
464 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
465 g_assert (!info->async_target);
466 info->async_target = target_func;
467 /* This is not GC tracked */
468 info->user_data = user_data;
472 The suspend lock is held during any suspend in progress.
473 A GC that has safepoints must take this lock as part of its
474 STW to make sure no unsafe pending suspend is in progress.
477 mono_thread_info_suspend_lock (void)
479 EnterCriticalSection (&global_suspend_lock);
483 mono_thread_info_suspend_unlock (void)
485 LeaveCriticalSection (&global_suspend_lock);
489 mono_thread_info_disable_new_interrupt (gboolean disable)
491 disable_new_interrupt = disable;
494 Disabled by default for now.
495 To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
496 which means mono-context and setup_async_callback, and we need a mono-threads backend.
499 mono_thread_info_new_interrupt_enabled (void)
501 /*We need STW gc events to work correctly*/
502 #if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
506 #if defined(HOST_WIN32)
509 #if defined (__i386__)
510 return !disable_new_interrupt;