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;
45 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
48 mono_hazard_pointer_clear (hp, 0);
50 mono_hazard_pointer_clear (hp, 1);
52 mono_hazard_pointer_clear (hp, 2);
56 If return non null Hazard Pointer 1 holds the return value.
59 mono_thread_info_lookup (MonoNativeThreadId id)
61 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
63 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
64 mono_hazard_pointer_clear_all (hp, -1);
68 mono_hazard_pointer_clear_all (hp, 1);
69 return mono_hazard_pointer_get_val (hp, 1);
73 mono_thread_info_insert (MonoThreadInfo *info)
75 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
77 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
78 mono_hazard_pointer_clear_all (hp, -1);
82 mono_hazard_pointer_clear_all (hp, -1);
87 mono_thread_info_remove (MonoThreadInfo *info)
89 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
92 THREADS_DEBUG ("removing info %p\n", info);
93 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
94 mono_hazard_pointer_clear_all (hp, -1);
99 free_thread_info (gpointer mem)
101 MonoThreadInfo *info = mem;
103 DeleteCriticalSection (&info->suspend_lock);
104 mono_threads_platform_free (info);
110 mono_thread_info_register_small_id (void)
112 int small_id = mono_thread_small_id_alloc ();
113 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
118 register_thread (MonoThreadInfo *info, gpointer baseptr)
120 int small_id = mono_thread_info_register_small_id ();
122 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
123 info->small_id = small_id;
125 InitializeCriticalSection (&info->suspend_lock);
127 /*set TLS early so SMR works */
128 mono_native_tls_set_value (thread_info_key, info);
130 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
132 if (threads_callbacks.thread_register) {
133 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
134 g_warning ("thread registation failed\n");
140 mono_threads_platform_register (info);
142 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
143 result = mono_thread_info_insert (info);
149 unregister_thread (void *arg)
151 MonoThreadInfo *info = arg;
152 int small_id = info->small_id;
155 THREADS_DEBUG ("unregistering info %p\n", info);
158 * TLS destruction order is not reliable so small_id might be cleaned up
161 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
164 The unregister callback is reposible for calling mono_threads_unregister_current_thread
165 since it usually needs to be done in sync with the GC does a stop-the-world.
167 if (threads_callbacks.thread_unregister)
168 threads_callbacks.thread_unregister (info);
170 mono_threads_unregister_current_thread (info);
172 /*now it's safe to free the thread info.*/
173 mono_thread_hazardous_free_or_queue (info, free_thread_info);
174 mono_thread_small_id_free (small_id);
178 * Removes the current thread from the thread list.
179 * This must be called from the thread unregister callback and nowhere else.
180 * The current thread must be passed as TLS might have already been cleaned up.
183 mono_threads_unregister_current_thread (MonoThreadInfo *info)
186 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
187 result = mono_thread_info_remove (info);
193 mono_thread_info_current (void)
195 return mono_native_tls_get_value (thread_info_key);
199 mono_thread_info_get_small_id (void)
201 gpointer val = mono_native_tls_get_value (small_id_key);
204 return GPOINTER_TO_INT (val) - 1;
208 mono_thread_info_list_head (void)
214 mono_thread_info_attach (void *baseptr)
216 MonoThreadInfo *info = mono_native_tls_get_value (thread_info_key);
218 info = g_malloc0 (thread_info_size);
219 THREADS_DEBUG ("attaching %p\n", info);
220 if (!register_thread (info, baseptr))
222 } else if (threads_callbacks.thread_attach) {
223 threads_callbacks.thread_attach (info);
229 mono_thread_info_dettach (void)
231 MonoThreadInfo *info = mono_native_tls_get_value (thread_info_key);
233 THREADS_DEBUG ("detaching %p\n", info);
234 unregister_thread (info);
239 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
242 threads_callbacks = *callbacks;
243 thread_info_size = info_size;
245 res = mono_native_tls_alloc (thread_info_key, NULL);
247 res = mono_native_tls_alloc (thread_info_key, unregister_thread);
251 res = mono_native_tls_alloc (small_id_key, NULL);
254 InitializeCriticalSection (&global_suspend_lock);
256 mono_lls_init (&thread_list, NULL);
257 mono_thread_smr_init ();
258 mono_threads_init_platform ();
260 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
264 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
266 runtime_callbacks = *callbacks;
269 MonoThreadInfoRuntimeCallbacks *
270 mono_threads_get_runtime_callbacks (void)
272 return &runtime_callbacks;
276 The return value is only valid until a matching mono_thread_info_resume is called
278 static MonoThreadInfo*
279 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
281 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
282 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
286 EnterCriticalSection (&info->suspend_lock);
288 /*thread is on the process of detaching*/
289 if (info->thread_state > STATE_RUNNING) {
290 mono_hazard_pointer_clear (hp, 1);
294 THREADS_DEBUG ("suspend %x IN COUNT %d\n", tid, info->suspend_count);
296 if (info->suspend_count) {
297 ++info->suspend_count;
298 mono_hazard_pointer_clear (hp, 1);
299 LeaveCriticalSection (&info->suspend_lock);
303 if (!mono_threads_core_suspend (info)) {
304 LeaveCriticalSection (&info->suspend_lock);
305 mono_hazard_pointer_clear (hp, 1);
309 if (interrupt_kernel)
310 mono_threads_core_interrupt (info);
312 ++info->suspend_count;
313 LeaveCriticalSection (&info->suspend_lock);
314 mono_hazard_pointer_clear (hp, 1);
320 mono_thread_info_self_suspend ()
322 MonoThreadInfo *info = mono_thread_info_current ();
326 EnterCriticalSection (&info->suspend_lock);
328 THREADS_DEBUG ("self suspend IN COUNT %d\n", info->suspend_count);
330 g_assert (info->suspend_count == 0);
331 ++info->suspend_count;
334 The internal API contract with this function is a bit out of the ordinary.
335 mono_threads_core_self_suspend executes with suspend_lock taken and must
336 release it after capturing the current context.
338 mono_threads_core_self_suspend (info);
342 mono_thread_info_resume (MonoNativeThreadId tid)
344 gboolean result = TRUE;
345 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
346 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
350 EnterCriticalSection (&info->suspend_lock);
352 THREADS_DEBUG ("resume %x IN COUNT %d\n",tid, info->suspend_count);
354 if (info->suspend_count <= 0) {
355 LeaveCriticalSection (&info->suspend_lock);
356 mono_hazard_pointer_clear (hp, 1);
361 * The theory here is that if we manage to suspend the thread it means it did not
362 * start cleanup since it take the same lock.
364 g_assert (mono_thread_info_get_tid (info));
366 if (--info->suspend_count == 0)
367 result = mono_threads_core_resume (info);
369 LeaveCriticalSection (&info->suspend_lock);
370 mono_hazard_pointer_clear (hp, 1);
376 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
377 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
380 is_thread_in_critical_region (MonoThreadInfo *info)
383 MonoJitInfo *ji = mono_jit_info_table_find (
384 info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN],
385 MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
392 return threads_callbacks.mono_method_is_critical (method);
397 If we are trying to suspend a target that is on a critical region
398 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
399 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
402 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
404 MonoThreadInfo *info = NULL;
405 int sleep_duration = 0;
407 /*FIXME: unify this with self-suspend*/
408 g_assert (id != mono_native_thread_id_get ());
410 mono_thread_info_suspend_lock ();
413 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel))) {
414 g_warning ("failed to suspend thread %p, hopefully it is dead", (gpointer)id);
415 mono_thread_info_suspend_unlock ();
418 /*WARNING: We now are in interrupt context until we resume the thread. */
419 if (!is_thread_in_critical_region (info))
422 if (!mono_thread_info_resume (id)) {
423 g_warning ("failed to result thread %p, hopefully it is dead", (gpointer)id);
424 mono_thread_info_suspend_unlock ();
427 THREADS_DEBUG ("restarted thread %p\n", (gpointer)id);
429 if (!sleep_duration) {
437 g_usleep (sleep_duration);
439 sleep_duration += 10;
442 mono_thread_info_suspend_unlock ();
447 Inject an assynchronous call into the target thread. The target thread must be suspended and
448 only a single async call can be setup for a given suspend cycle.
449 This async call must cause stack unwinding as the current implementation doesn't save enough state
450 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
451 currently used only to deliver exceptions.
454 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
456 g_assert (info->suspend_count);
457 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
458 g_assert (!info->async_target);
459 info->async_target = target_func;
460 /* This is not GC tracked */
461 info->user_data = user_data;
465 The suspend lock is held during any suspend in progress.
466 A GC that has safepoints must take this lock as part of its
467 STW to make sure no unsafe pending suspend is in progress.
470 mono_thread_info_suspend_lock (void)
472 EnterCriticalSection (&global_suspend_lock);
476 mono_thread_info_suspend_unlock (void)
478 LeaveCriticalSection (&global_suspend_lock);
482 Disabled by default for now.
483 To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
484 which means mono-context and setup_async_callback, and we need a mono-threads backend.
487 mono_thread_info_new_interrupt_enabled (void)
489 /*We need STW gc events to work correctly*/
490 #if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
494 #if defined(HOST_WIN32)
497 #if defined (__i386__)