Merge pull request #3481 from alexrp/master
[mono-project.git] / mono / utils / mono-threads.c
blob12c95c7861bda5eb2bb8f92d3efce2e72dac577d
1 /*
2 * mono-threads.c: Low-level threading
4 * Author:
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * Copyright 2011 Novell, Inc (http://www.novell.com)
8 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <config.h>
14 /* enable pthread extensions */
15 #ifdef TARGET_MACH
16 #define _DARWIN_C_SOURCE
17 #endif
19 #include <mono/utils/mono-compiler.h>
20 #include <mono/utils/mono-os-semaphore.h>
21 #include <mono/utils/mono-threads.h>
22 #include <mono/utils/mono-tls.h>
23 #include <mono/utils/hazard-pointer.h>
24 #include <mono/utils/mono-memory-model.h>
25 #include <mono/utils/mono-mmap.h>
26 #include <mono/utils/atomic.h>
27 #include <mono/utils/mono-time.h>
28 #include <mono/utils/mono-lazy-init.h>
29 #include <mono/utils/mono-coop-mutex.h>
30 #include <mono/utils/mono-coop-semaphore.h>
31 #include <mono/utils/mono-threads-coop.h>
33 #include <errno.h>
35 #if defined(__MACH__)
36 #include <mono/utils/mach-support.h>
37 #endif
40 Mutex that makes sure only a single thread can be suspending others.
41 Suspend is a very racy operation since it requires restarting until
42 the target thread is not on an unsafe region.
44 We could implement this using critical regions, but would be much much
45 harder for an operation that is hardly performance critical.
47 The GC has to acquire this lock before starting a STW to make sure
48 a runtime suspend won't make it wronly see a thread in a safepoint
49 when it is in fact not.
51 This has to be a naked locking primitive, and not a coop aware one, as
52 it needs to be usable when destroying thread_info_key, the TLS key for
53 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
54 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
55 to an assertion error. We then simply switch state manually in
56 mono_thread_info_suspend_lock_with_info.
58 static MonoSemType global_suspend_semaphore;
60 static size_t thread_info_size;
61 static MonoThreadInfoCallbacks threads_callbacks;
62 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
63 static MonoNativeTlsKey thread_info_key, thread_exited_key;
64 #ifdef HAVE_KW_THREAD
65 static __thread guint32 tls_small_id MONO_TLS_FAST;
66 #else
67 static MonoNativeTlsKey small_id_key;
68 #endif
69 static MonoLinkedListSet thread_list;
70 static gboolean mono_threads_inited = FALSE;
72 static MonoSemType suspend_semaphore;
73 static size_t pending_suspends;
74 static gboolean unified_suspend_enabled;
76 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
78 /*warn at 50 ms*/
79 #define SLEEP_DURATION_BEFORE_WARNING (50)
80 /*never aborts */
81 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
83 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
84 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
86 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
88 void
89 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
91 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
92 InterlockedIncrement (&abort_posts);
93 mono_os_sem_post (&suspend_semaphore);
96 void
97 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
99 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
100 InterlockedIncrement (&suspend_posts);
101 mono_os_sem_post (&suspend_semaphore);
104 void
105 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
107 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
108 InterlockedIncrement (&resume_posts);
109 mono_os_sem_post (&suspend_semaphore);
112 static gboolean
113 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
115 if (mono_threads_is_coop_enabled ()) {
116 /* There's nothing else to do after we async request the thread to suspend */
117 mono_threads_add_to_pending_operation_set (info);
118 return TRUE;
121 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
124 static gboolean
125 check_async_suspend (MonoThreadInfo *info)
127 if (mono_threads_is_coop_enabled ()) {
128 /* Async suspend can't async fail on coop */
129 return TRUE;
132 return mono_threads_suspend_check_suspend_result (info);
135 static void
136 resume_async_suspended (MonoThreadInfo *info)
138 if (mono_threads_is_coop_enabled ())
139 g_assert_not_reached ();
141 g_assert (mono_threads_suspend_begin_async_resume (info));
144 static void
145 resume_self_suspended (MonoThreadInfo* info)
147 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
148 mono_os_sem_post (&info->resume_semaphore);
151 void
152 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
154 int res;
155 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
156 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
157 g_assert (res != -1);
160 static void
161 resume_blocking_suspended (MonoThreadInfo* info)
163 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
164 mono_os_sem_post (&info->resume_semaphore);
167 void
168 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
170 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
171 ++pending_suspends;
172 InterlockedIncrement (&pending_ops);
175 void
176 mono_threads_begin_global_suspend (void)
178 size_t ps = pending_suspends;
179 if (G_UNLIKELY (ps != 0))
180 g_error ("pending_suspends = %d, but must be 0", ps);
181 THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d (sp + rp + ap == wd) (wd == po)\n", suspend_posts, resume_posts,
182 abort_posts, waits_done, pending_ops);
183 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
184 mono_threads_coop_begin_global_suspend ();
187 void
188 mono_threads_end_global_suspend (void)
190 size_t ps = pending_suspends;
191 if (G_UNLIKELY (ps != 0))
192 g_error ("pending_suspends = %d, but must be 0", ps);
193 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
194 abort_posts, waits_done, pending_ops);
195 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
196 mono_threads_coop_end_global_suspend ();
199 static void
200 dump_threads (void)
202 MonoThreadInfo *cur = mono_thread_info_current ();
204 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
205 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
206 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
207 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
208 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
209 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
215 FOREACH_THREAD_SAFE (info) {
216 #ifdef TARGET_MACH
217 char thread_name [256] = { 0 };
218 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
220 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] (%s) state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, thread_name, info->thread_state, info == cur ? "GC INITIATOR" : "" );
221 #else
222 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
223 #endif
224 } FOREACH_THREAD_SAFE_END
227 gboolean
228 mono_threads_wait_pending_operations (void)
230 int i;
231 int c = pending_suspends;
233 /* Wait threads to park */
234 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
235 if (pending_suspends) {
236 MonoStopwatch suspension_time;
237 mono_stopwatch_start (&suspension_time);
238 for (i = 0; i < pending_suspends; ++i) {
239 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
240 InterlockedIncrement (&waits_done);
241 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
242 continue;
243 mono_stopwatch_stop (&suspension_time);
245 dump_threads ();
247 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
248 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
250 mono_stopwatch_stop (&suspension_time);
251 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
255 pending_suspends = 0;
257 return c > 0;
261 //Thread initialization code
263 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
265 static inline void
266 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
268 if (retain != 0)
269 mono_hazard_pointer_clear (hp, 0);
270 if (retain != 1)
271 mono_hazard_pointer_clear (hp, 1);
272 if (retain != 2)
273 mono_hazard_pointer_clear (hp, 2);
277 If return non null Hazard Pointer 1 holds the return value.
279 MonoThreadInfo*
280 mono_thread_info_lookup (MonoNativeThreadId id)
282 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
284 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
285 mono_hazard_pointer_clear_all (hp, -1);
286 return NULL;
289 mono_hazard_pointer_clear_all (hp, 1);
290 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
293 static gboolean
294 mono_thread_info_insert (MonoThreadInfo *info)
296 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
298 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
299 mono_hazard_pointer_clear_all (hp, -1);
300 return FALSE;
303 mono_hazard_pointer_clear_all (hp, -1);
304 return TRUE;
307 static gboolean
308 mono_thread_info_remove (MonoThreadInfo *info)
310 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
311 gboolean res;
313 THREADS_DEBUG ("removing info %p\n", info);
314 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
315 mono_hazard_pointer_clear_all (hp, -1);
316 return res;
319 static void
320 free_thread_info (gpointer mem)
322 MonoThreadInfo *info = (MonoThreadInfo *) mem;
324 mono_os_sem_destroy (&info->resume_semaphore);
325 mono_threads_suspend_free (info);
327 g_free (info);
331 mono_thread_info_register_small_id (void)
333 int small_id = mono_thread_small_id_alloc ();
334 #ifdef HAVE_KW_THREAD
335 tls_small_id = small_id;
336 #else
337 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
338 #endif
339 return small_id;
342 static void*
343 register_thread (MonoThreadInfo *info, gpointer baseptr)
345 size_t stsize = 0;
346 guint8 *staddr = NULL;
347 int small_id = mono_thread_info_register_small_id ();
348 gboolean result;
349 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
350 info->small_id = small_id;
352 mono_os_sem_init (&info->resume_semaphore, 0);
354 /*set TLS early so SMR works */
355 mono_native_tls_set_value (thread_info_key, info);
357 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
359 if (threads_callbacks.thread_register) {
360 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
361 // g_warning ("thread registation failed\n");
362 mono_native_tls_set_value (thread_info_key, NULL);
363 g_free (info);
364 return NULL;
368 mono_thread_info_get_stack_bounds (&staddr, &stsize);
369 g_assert (staddr);
370 g_assert (stsize);
371 info->stack_start_limit = staddr;
372 info->stack_end = staddr + stsize;
374 info->stackdata = g_byte_array_new ();
376 mono_threads_platform_register (info);
377 mono_threads_suspend_register (info);
380 Transition it before taking any locks or publishing itself to reduce the chance
381 of others witnessing a detached thread.
382 We can reasonably expect that until this thread gets published, no other thread will
383 try to manipulate it.
385 mono_threads_transition_attach (info);
386 mono_thread_info_suspend_lock ();
387 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
388 result = mono_thread_info_insert (info);
389 g_assert (result);
390 mono_thread_info_suspend_unlock ();
391 return info;
394 static void
395 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
397 static void
398 unregister_thread (void *arg)
400 gpointer gc_unsafe_stackdata;
401 MonoThreadInfo *info;
402 int small_id;
404 info = (MonoThreadInfo *) arg;
405 g_assert (info);
406 g_assert (mono_thread_info_is_current (info));
407 g_assert (mono_thread_info_is_live (info));
409 small_id = info->small_id;
411 /* We only enter the GC unsafe region, as when exiting this function, the thread
412 * will be detached, and the current MonoThreadInfo* will be destroyed. */
413 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
415 THREADS_DEBUG ("unregistering info %p\n", info);
417 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
419 mono_threads_platform_unregister (info);
422 * TLS destruction order is not reliable so small_id might be cleaned up
423 * before us.
425 #ifndef HAVE_KW_THREAD
426 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
427 #endif
430 First perform the callback that requires no locks.
431 This callback has the potential of taking other locks, so we do it before.
432 After it completes, the thread remains functional.
434 if (threads_callbacks.thread_detach)
435 threads_callbacks.thread_detach (info);
437 mono_thread_info_suspend_lock_with_info (info);
440 Now perform the callback that must be done under locks.
441 This will render the thread useless and non-suspendable, so it must
442 be done while holding the suspend lock to give no other thread chance
443 to suspend it.
445 if (threads_callbacks.thread_unregister)
446 threads_callbacks.thread_unregister (info);
447 mono_threads_unregister_current_thread (info);
448 mono_threads_transition_detach (info);
450 mono_thread_info_suspend_unlock ();
452 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
454 /*now it's safe to free the thread info.*/
455 mono_thread_hazardous_try_free (info, free_thread_info);
456 /* Pump the HP queue */
457 mono_thread_hazardous_try_free_some ();
459 mono_thread_small_id_free (small_id);
462 static void
463 thread_exited_dtor (void *arg)
465 #if defined(__MACH__)
467 * Since we use pthread dtors to clean up thread data, if a thread
468 * is attached to the runtime by another pthread dtor after our dtor
469 * has ran, it will never be detached, leading to various problems
470 * since the thread ids etc. will be reused while they are still in
471 * the threads hashtables etc.
472 * Dtors are called in a loop until all user tls entries are 0,
473 * but the loop has a maximum count (4), so if we set the tls
474 * variable every time, it will remain set when system tls dtors
475 * are ran. This allows mono_thread_info_is_exiting () to detect
476 * whenever the thread is exiting, even if it is executed from a
477 * system tls dtor (i.e. obj-c dealloc methods).
479 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
480 #endif
484 * Removes the current thread from the thread list.
485 * This must be called from the thread unregister callback and nowhere else.
486 * The current thread must be passed as TLS might have already been cleaned up.
488 static void
489 mono_threads_unregister_current_thread (MonoThreadInfo *info)
491 gboolean result;
492 g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
493 result = mono_thread_info_remove (info);
494 g_assert (result);
497 MonoThreadInfo*
498 mono_thread_info_current_unchecked (void)
500 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
504 MonoThreadInfo*
505 mono_thread_info_current (void)
507 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
508 if (info)
509 return info;
511 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
514 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
515 The way to distinguish between before, during and after cleanup is the following:
517 -If the TLS key is set, cleanup has not begun;
518 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
519 -If the thread is nowhere to be found, cleanup has finished.
521 We cannot function after cleanup since there's no way to ensure what will happen.
523 g_assert (info);
525 /*We're looking up the current thread which will not be freed until we finish running, so no need to keep it on a HP */
526 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
528 return info;
532 mono_thread_info_get_small_id (void)
534 #ifdef HAVE_KW_THREAD
535 return tls_small_id;
536 #else
537 gpointer val = mono_native_tls_get_value (small_id_key);
538 if (!val)
539 return -1;
540 return GPOINTER_TO_INT (val) - 1;
541 #endif
544 MonoLinkedListSet*
545 mono_thread_info_list_head (void)
547 return &thread_list;
551 * mono_threads_attach_tools_thread
553 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
555 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
556 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
557 * the managed heap.
559 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
560 * doing things like resolving backtraces in their background processing thread.
562 void
563 mono_threads_attach_tools_thread (void)
565 int dummy = 0;
566 MonoThreadInfo *info;
568 /* Must only be called once */
569 g_assert (!mono_native_tls_get_value (thread_info_key));
571 while (!mono_threads_inited) {
572 mono_thread_info_usleep (10);
575 info = mono_thread_info_attach (&dummy);
576 g_assert (info);
578 info->tools_thread = TRUE;
581 MonoThreadInfo*
582 mono_thread_info_attach (void *baseptr)
584 MonoThreadInfo *info;
585 if (!mono_threads_inited)
587 #ifdef HOST_WIN32
588 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
589 * thread is created before an embedding API user initialized Mono. */
590 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
591 return NULL;
592 #else
593 g_assert (mono_threads_inited);
594 #endif
596 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
597 if (!info) {
598 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
599 THREADS_DEBUG ("attaching %p\n", info);
600 if (!register_thread (info, baseptr))
601 return NULL;
602 } else if (threads_callbacks.thread_attach) {
603 threads_callbacks.thread_attach (info);
605 return info;
608 void
609 mono_thread_info_detach (void)
611 MonoThreadInfo *info;
612 if (!mono_threads_inited)
614 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
615 * is created before an embedding API user initialized Mono. */
616 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
617 return;
619 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
620 if (info) {
621 THREADS_DEBUG ("detaching %p\n", info);
622 unregister_thread (info);
623 mono_native_tls_set_value (thread_info_key, NULL);
628 * mono_thread_info_is_exiting:
630 * Return whenever the current thread is exiting, i.e. it is running pthread
631 * dtors.
633 gboolean
634 mono_thread_info_is_exiting (void)
636 #if defined(__MACH__)
637 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
638 return TRUE;
639 #endif
640 return FALSE;
643 #ifndef HOST_WIN32
644 static void
645 thread_info_key_dtor (void *arg)
647 /* Put the MonoThreadInfo back for the duration of the
648 * unregister code. In some circumstances the thread needs to
649 * take the GC lock which may block which requires a coop
650 * state transition. */
651 mono_native_tls_set_value (thread_info_key, arg);
652 unregister_thread (arg);
653 mono_native_tls_set_value (thread_info_key, NULL);
655 #endif
657 void
658 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
660 gboolean res;
661 threads_callbacks = *callbacks;
662 thread_info_size = info_size;
663 const char *sleepLimit;
664 #ifdef HOST_WIN32
665 res = mono_native_tls_alloc (&thread_info_key, NULL);
666 res = mono_native_tls_alloc (&thread_exited_key, NULL);
667 #else
668 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
669 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
670 #endif
672 g_assert (res);
674 #ifndef HAVE_KW_THREAD
675 res = mono_native_tls_alloc (&small_id_key, NULL);
676 #endif
677 g_assert (res);
679 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
681 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
682 errno = 0;
683 long threshold = strtol(sleepLimit, NULL, 10);
684 if ((errno == 0) && (threshold >= 40)) {
685 sleepAbortDuration = threshold;
686 sleepWarnDuration = threshold / 20;
687 } else
688 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
691 mono_os_sem_init (&global_suspend_semaphore, 1);
692 mono_os_sem_init (&suspend_semaphore, 0);
694 mono_lls_init (&thread_list, NULL);
695 mono_thread_smr_init ();
696 mono_threads_platform_init ();
697 mono_threads_suspend_init ();
698 mono_threads_coop_init ();
699 mono_threads_abort_syscall_init ();
701 #if defined(__MACH__)
702 mono_mach_init (thread_info_key);
703 #endif
705 mono_threads_inited = TRUE;
707 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
710 void
711 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
713 runtime_callbacks = *callbacks;
716 MonoThreadInfoRuntimeCallbacks *
717 mono_threads_get_runtime_callbacks (void)
719 return &runtime_callbacks;
723 Signal that the current thread wants to be suspended.
724 This function can be called without holding the suspend lock held.
725 To finish suspending, call mono_suspend_check.
727 void
728 mono_thread_info_begin_self_suspend (void)
730 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
731 if (!info)
732 return;
734 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
735 mono_threads_transition_request_self_suspension (info);
738 void
739 mono_thread_info_end_self_suspend (void)
741 MonoThreadInfo *info;
743 info = mono_thread_info_current ();
744 if (!info)
745 return;
746 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
748 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
750 /* commit the saved state and notify others if needed */
751 switch (mono_threads_transition_state_poll (info)) {
752 case SelfSuspendResumed:
753 return;
754 case SelfSuspendWait:
755 mono_thread_info_wait_for_resume (info);
756 break;
757 case SelfSuspendNotifyAndWait:
758 mono_threads_notify_initiator_of_suspend (info);
759 mono_thread_info_wait_for_resume (info);
760 mono_threads_notify_initiator_of_resume (info);
761 break;
765 static gboolean
766 mono_thread_info_core_resume (MonoThreadInfo *info)
768 gboolean res = FALSE;
769 if (info->create_suspended) {
770 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
771 /* Have to special case this, as the normal suspend/resume pair are racy, they don't work if he resume is received before the suspend */
772 info->create_suspended = FALSE;
773 mono_coop_sem_post (&info->create_suspended_sem);
774 return TRUE;
777 switch (mono_threads_transition_request_resume (info)) {
778 case ResumeError:
779 res = FALSE;
780 break;
781 case ResumeOk:
782 res = TRUE;
783 break;
784 case ResumeInitSelfResume:
785 resume_self_suspended (info);
786 res = TRUE;
787 break;
788 case ResumeInitAsyncResume:
789 resume_async_suspended (info);
790 res = TRUE;
791 break;
792 case ResumeInitBlockingResume:
793 resume_blocking_suspended (info);
794 res = TRUE;
795 break;
798 return res;
801 gboolean
802 mono_thread_info_resume (MonoNativeThreadId tid)
804 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
805 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
806 MonoThreadInfo *info;
808 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
810 mono_thread_info_suspend_lock ();
812 info = mono_thread_info_lookup (tid); /*info on HP1*/
813 if (!info) {
814 result = FALSE;
815 goto cleanup;
818 result = mono_thread_info_core_resume (info);
820 //Wait for the pending resume to finish
821 mono_threads_wait_pending_operations ();
823 cleanup:
824 mono_thread_info_suspend_unlock ();
825 mono_hazard_pointer_clear (hp, 1);
826 return result;
829 gboolean
830 mono_thread_info_begin_suspend (MonoThreadInfo *info)
832 switch (mono_threads_transition_request_async_suspension (info)) {
833 case AsyncSuspendAlreadySuspended:
834 case AsyncSuspendBlocking:
835 return TRUE;
836 case AsyncSuspendWait:
837 mono_threads_add_to_pending_operation_set (info);
838 return TRUE;
839 case AsyncSuspendInitSuspend:
840 return begin_async_suspend (info, FALSE);
841 default:
842 g_assert_not_reached ();
846 gboolean
847 mono_thread_info_begin_resume (MonoThreadInfo *info)
849 return mono_thread_info_core_resume (info);
853 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
854 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
856 static gboolean
857 is_thread_in_critical_region (MonoThreadInfo *info)
859 MonoMethod *method;
860 MonoJitInfo *ji;
861 gpointer stack_start;
862 MonoThreadUnwindState *state;
864 /* Are we inside a system critical region? */
865 if (info->inside_critical_region)
866 return TRUE;
868 /* Are we inside a GC critical region? */
869 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
870 return TRUE;
873 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
874 state = mono_thread_info_get_suspend_state (info);
875 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
876 return FALSE;
878 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
879 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
880 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
881 return TRUE;
883 ji = mono_jit_info_table_find (
884 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
885 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
887 if (!ji)
888 return FALSE;
890 method = mono_jit_info_get_method (ji);
892 return threads_callbacks.mono_method_is_critical (method);
895 gboolean
896 mono_thread_info_in_critical_location (MonoThreadInfo *info)
898 return is_thread_in_critical_region (info);
902 The return value is only valid until a matching mono_thread_info_resume is called
904 static MonoThreadInfo*
905 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
907 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
908 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
909 if (!info)
910 return NULL;
912 switch (mono_threads_transition_request_async_suspension (info)) {
913 case AsyncSuspendAlreadySuspended:
914 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
915 return info;
916 case AsyncSuspendWait:
917 mono_threads_add_to_pending_operation_set (info);
918 break;
919 case AsyncSuspendInitSuspend:
920 if (!begin_async_suspend (info, interrupt_kernel)) {
921 mono_hazard_pointer_clear (hp, 1);
922 return NULL;
924 break;
925 case AsyncSuspendBlocking:
926 if (interrupt_kernel)
927 mono_threads_suspend_abort_syscall (info);
929 break;
930 default:
931 g_assert_not_reached ();
934 //Wait for the pending suspend to finish
935 mono_threads_wait_pending_operations ();
937 if (!check_async_suspend (info)) {
938 mono_thread_info_core_resume (info);
939 mono_threads_wait_pending_operations ();
940 mono_hazard_pointer_clear (hp, 1);
941 return NULL;
943 return info;
946 static MonoThreadInfo*
947 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
949 MonoThreadInfo *info = NULL;
950 int sleep_duration = 0;
951 for (;;) {
952 if (!(info = suspend_sync (id, interrupt_kernel))) {
953 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
954 return NULL;
957 /*WARNING: We now are in interrupt context until we resume the thread. */
958 if (!is_thread_in_critical_region (info))
959 break;
961 if (!mono_thread_info_core_resume (info)) {
962 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
963 return NULL;
965 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
967 /* Wait for the pending resume to finish */
968 mono_threads_wait_pending_operations ();
970 if (sleep_duration == 0)
971 mono_thread_info_yield ();
972 else
973 g_usleep (sleep_duration);
975 sleep_duration += 10;
977 return info;
980 void
981 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
983 int result;
984 MonoThreadInfo *info = NULL;
985 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
987 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
988 /*FIXME: unify this with self-suspend*/
989 g_assert (id != mono_native_thread_id_get ());
991 /* This can block during stw */
992 mono_thread_info_suspend_lock ();
993 mono_threads_begin_global_suspend ();
995 info = suspend_sync_nolock (id, interrupt_kernel);
996 if (!info)
997 goto done;
999 switch (result = callback (info, user_data)) {
1000 case MonoResumeThread:
1001 mono_hazard_pointer_set (hp, 1, info);
1002 mono_thread_info_core_resume (info);
1003 mono_threads_wait_pending_operations ();
1004 break;
1005 case KeepSuspended:
1006 break;
1007 default:
1008 g_error ("Invalid suspend_and_run callback return value %d", result);
1011 done:
1012 mono_hazard_pointer_clear (hp, 1);
1013 mono_threads_end_global_suspend ();
1014 mono_thread_info_suspend_unlock ();
1018 Inject an assynchronous call into the target thread. The target thread must be suspended and
1019 only a single async call can be setup for a given suspend cycle.
1020 This async call must cause stack unwinding as the current implementation doesn't save enough state
1021 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1022 currently used only to deliver exceptions.
1024 void
1025 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1027 /* An async call can only be setup on an async suspended thread */
1028 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1029 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1030 g_assert (!info->async_target);
1031 info->async_target = target_func;
1032 /* This is not GC tracked */
1033 info->user_data = user_data;
1037 The suspend lock is held during any suspend in progress.
1038 A GC that has safepoints must take this lock as part of its
1039 STW to make sure no unsafe pending suspend is in progress.
1042 static void
1043 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1045 g_assert (info);
1046 g_assert (mono_thread_info_is_current (info));
1047 g_assert (mono_thread_info_is_live (info));
1049 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1051 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1052 g_assert (res != -1);
1054 MONO_EXIT_GC_SAFE_WITH_INFO;
1057 void
1058 mono_thread_info_suspend_lock (void)
1060 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1063 void
1064 mono_thread_info_suspend_unlock (void)
1066 mono_os_sem_post (&global_suspend_semaphore);
1070 * This is a very specific function whose only purpose is to
1071 * break a given thread from socket syscalls.
1073 * This only exists because linux won't fail a call to connect
1074 * if the underlying is closed.
1076 * TODO We should cleanup and unify this with the other syscall abort
1077 * facility.
1079 void
1080 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1082 MonoThreadHazardPointers *hp;
1083 MonoThreadInfo *info;
1085 if (tid == mono_native_thread_id_get () || !mono_threads_suspend_needs_abort_syscall ())
1086 return;
1088 hp = mono_hazard_pointer_get ();
1089 info = mono_thread_info_lookup (tid);
1090 if (!info)
1091 return;
1093 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1094 mono_hazard_pointer_clear (hp, 1);
1095 return;
1098 mono_thread_info_suspend_lock ();
1099 mono_threads_begin_global_suspend ();
1101 mono_threads_suspend_abort_syscall (info);
1102 mono_threads_wait_pending_operations ();
1104 mono_hazard_pointer_clear (hp, 1);
1106 mono_threads_end_global_suspend ();
1107 mono_thread_info_suspend_unlock ();
1110 gboolean
1111 mono_thread_info_unified_management_enabled (void)
1113 return unified_suspend_enabled;
1117 * mono_thread_info_set_is_async_context:
1119 * Set whenever the current thread is in an async context. Some runtime functions might behave
1120 * differently while in an async context in order to be async safe.
1122 void
1123 mono_thread_info_set_is_async_context (gboolean async_context)
1125 MonoThreadInfo *info = mono_thread_info_current ();
1127 if (info)
1128 info->is_async_context = async_context;
1131 gboolean
1132 mono_thread_info_is_async_context (void)
1134 MonoThreadInfo *info = mono_thread_info_current ();
1136 if (info)
1137 return info->is_async_context;
1138 else
1139 return FALSE;
1143 * mono_threads_create_thread:
1145 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1146 * Returns: a windows or io-layer handle for the thread.
1148 HANDLE
1149 mono_threads_create_thread (MonoThreadStart start, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
1151 return mono_threads_platform_create_thread (start, arg, tp, out_tid);
1155 * mono_thread_info_get_stack_bounds:
1157 * Return the address and size of the current threads stack. Return NULL as the
1158 * stack address if the stack address cannot be determined.
1160 void
1161 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1163 guint8 *current = (guint8 *)&stsize;
1164 mono_threads_platform_get_stack_bounds (staddr, stsize);
1165 if (!*staddr)
1166 return;
1168 /* Sanity check the result */
1169 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1171 /* When running under emacs, sometimes staddr is not aligned to a page size */
1172 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1175 gboolean
1176 mono_thread_info_yield (void)
1178 return mono_threads_platform_yield ();
1180 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1181 static MonoCoopMutex sleep_mutex;
1182 static MonoCoopCond sleep_cond;
1184 static void
1185 sleep_initialize (void)
1187 mono_coop_mutex_init (&sleep_mutex);
1188 mono_coop_cond_init (&sleep_cond);
1191 static void
1192 sleep_interrupt (gpointer data)
1194 mono_coop_mutex_lock (&sleep_mutex);
1195 mono_coop_cond_broadcast (&sleep_cond);
1196 mono_coop_mutex_unlock (&sleep_mutex);
1199 static inline guint32
1200 sleep_interruptable (guint32 ms, gboolean *alerted)
1202 gint64 now, end;
1204 g_assert (INFINITE == G_MAXUINT32);
1206 g_assert (alerted);
1207 *alerted = FALSE;
1209 if (ms != INFINITE)
1210 end = mono_100ns_ticks () + (ms * 1000 * 10);
1212 mono_lazy_initialize (&sleep_init, sleep_initialize);
1214 mono_coop_mutex_lock (&sleep_mutex);
1216 for (;;) {
1217 if (ms != INFINITE) {
1218 now = mono_100ns_ticks ();
1219 if (now > end)
1220 break;
1223 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1224 if (*alerted) {
1225 mono_coop_mutex_unlock (&sleep_mutex);
1226 return WAIT_IO_COMPLETION;
1229 if (ms != INFINITE)
1230 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, (end - now) / 10 / 1000);
1231 else
1232 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1234 mono_thread_info_uninstall_interrupt (alerted);
1235 if (*alerted) {
1236 mono_coop_mutex_unlock (&sleep_mutex);
1237 return WAIT_IO_COMPLETION;
1241 mono_coop_mutex_unlock (&sleep_mutex);
1243 return 0;
1246 gint
1247 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1249 if (ms == 0) {
1250 MonoThreadInfo *info;
1252 mono_thread_info_yield ();
1254 info = mono_thread_info_current ();
1255 if (info && mono_thread_info_is_interrupt_state (info))
1256 return WAIT_IO_COMPLETION;
1258 return 0;
1261 if (alerted)
1262 return sleep_interruptable (ms, alerted);
1264 MONO_ENTER_GC_SAFE;
1266 if (ms == INFINITE) {
1267 do {
1268 #ifdef HOST_WIN32
1269 Sleep (G_MAXUINT32);
1270 #else
1271 sleep (G_MAXUINT32);
1272 #endif
1273 } while (1);
1274 } else {
1275 int ret;
1276 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1277 struct timespec start, target;
1279 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1280 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1281 g_assert (ret == 0);
1283 target = start;
1284 target.tv_sec += ms / 1000;
1285 target.tv_nsec += (ms % 1000) * 1000000;
1286 if (target.tv_nsec > 999999999) {
1287 target.tv_nsec -= 999999999;
1288 target.tv_sec ++;
1291 do {
1292 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1293 } while (ret != 0);
1294 #elif HOST_WIN32
1295 Sleep (ms);
1296 #else
1297 struct timespec req, rem;
1299 req.tv_sec = ms / 1000;
1300 req.tv_nsec = (ms % 1000) * 1000000;
1302 do {
1303 memset (&rem, 0, sizeof (rem));
1304 ret = nanosleep (&req, &rem);
1305 } while (ret != 0);
1306 #endif /* __linux__ */
1309 MONO_EXIT_GC_SAFE;
1311 return 0;
1314 gint
1315 mono_thread_info_usleep (guint64 us)
1317 MONO_ENTER_GC_SAFE;
1318 g_usleep (us);
1319 MONO_EXIT_GC_SAFE;
1320 return 0;
1323 gpointer
1324 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1326 return ((MonoThreadInfo*)info)->tls [key];
1330 * mono_threads_info_tls_set:
1332 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1333 * values of TLS variables for threads other than the current thread.
1334 * This should only be used for infrequently changing TLS variables, and it should
1335 * be paired with setting the real TLS variable since this provides no GC tracking.
1337 void
1338 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1340 ((MonoThreadInfo*)info)->tls [key] = value;
1344 * mono_thread_info_exit:
1346 * Exit the current thread.
1347 * This function doesn't return.
1349 void
1350 mono_thread_info_exit (void)
1352 mono_threads_platform_exit (0);
1356 * mono_threads_open_thread_handle:
1358 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1359 * The handle need to be closed by calling CloseHandle () when it is no
1360 * longer needed.
1362 HANDLE
1363 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1365 return mono_threads_platform_open_thread_handle (handle, tid);
1368 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1370 struct _MonoThreadInfoInterruptToken {
1371 void (*callback) (gpointer data);
1372 gpointer data;
1376 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1378 * - @callback: must be able to be called from another thread and always cancel the wait
1379 * - @data: passed to the callback
1380 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1381 * if set to TRUE, it must mean that the thread is in interrupted state
1383 void
1384 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1386 MonoThreadInfo *info;
1387 MonoThreadInfoInterruptToken *previous_token, *token;
1389 g_assert (callback);
1391 g_assert (interrupted);
1392 *interrupted = FALSE;
1394 info = mono_thread_info_current ();
1395 g_assert (info);
1397 /* The memory of this token can be freed at 2 places:
1398 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1399 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1400 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1401 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1402 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1403 token->callback = callback;
1404 token->data = data;
1406 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1408 if (previous_token) {
1409 if (previous_token != INTERRUPT_STATE)
1410 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1412 g_free (token);
1414 *interrupted = TRUE;
1417 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1418 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1421 void
1422 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1424 MonoThreadInfo *info;
1425 MonoThreadInfoInterruptToken *previous_token;
1427 g_assert (interrupted);
1428 *interrupted = FALSE;
1430 info = mono_thread_info_current ();
1431 g_assert (info);
1433 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1435 /* only the installer can uninstall the token */
1436 g_assert (previous_token);
1438 if (previous_token == INTERRUPT_STATE) {
1439 /* if it is interrupted, then it is going to be freed in finish interrupt */
1440 *interrupted = TRUE;
1441 } else {
1442 g_free (previous_token);
1445 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1446 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1449 static MonoThreadInfoInterruptToken*
1450 set_interrupt_state (MonoThreadInfo *info)
1452 MonoThreadInfoInterruptToken *token, *previous_token;
1454 g_assert (info);
1456 /* Atomically obtain the token the thread is
1457 * waiting on, and change it to a flag value. */
1459 do {
1460 previous_token = info->interrupt_token;
1462 /* Already interrupted */
1463 if (previous_token == INTERRUPT_STATE) {
1464 token = NULL;
1465 break;
1468 token = previous_token;
1469 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1471 return token;
1475 * mono_thread_info_prepare_interrupt:
1477 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1478 * - if the thread calls one of the WaitFor functions, the function will return with
1479 * WAIT_IO_COMPLETION instead of waiting
1480 * - if the thread was waiting when this function was called, the wait will be broken
1482 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1483 * didn't receive the interrupt signal yet, in this case it should call the wait function
1484 * again. This essentially means that the target thread will busy wait until it is ready to
1485 * process the interruption.
1487 MonoThreadInfoInterruptToken*
1488 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1490 MonoThreadInfoInterruptToken *token;
1492 token = set_interrupt_state (info);
1494 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1495 mono_thread_info_get_tid (info), token);
1497 return token;
1500 void
1501 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1503 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1505 if (token == NULL)
1506 return;
1508 g_assert (token->callback);
1510 token->callback (token->data);
1512 g_free (token);
1515 void
1516 mono_thread_info_self_interrupt (void)
1518 MonoThreadInfo *info;
1519 MonoThreadInfoInterruptToken *token;
1521 info = mono_thread_info_current ();
1522 g_assert (info);
1524 token = set_interrupt_state (info);
1525 g_assert (!token);
1527 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1528 mono_thread_info_get_tid (info));
1531 /* Clear the interrupted flag of the current thread, set with
1532 * mono_thread_info_self_interrupt, so it can wait again */
1533 void
1534 mono_thread_info_clear_self_interrupt ()
1536 MonoThreadInfo *info;
1537 MonoThreadInfoInterruptToken *previous_token;
1539 info = mono_thread_info_current ();
1540 g_assert (info);
1542 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1543 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1545 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1548 gboolean
1549 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1551 g_assert (info);
1552 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1555 void
1556 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1558 g_assert (info);
1560 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1561 g_string_append_printf (text, "not waiting");
1562 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1563 g_string_append_printf (text, "interrupted state");
1564 else
1565 g_string_append_printf (text, "waiting");
1568 gboolean
1569 mono_thread_info_is_current (MonoThreadInfo *info)
1571 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1574 void
1575 mono_thread_info_set_exited (THREAD_INFO_TYPE *info)
1577 g_assert (mono_thread_info_is_current (info));
1578 mono_threads_platform_set_exited (info);
1581 gpointer
1582 mono_thread_info_get_handle (THREAD_INFO_TYPE *info)
1584 g_assert (info->handle);
1585 return info->handle;
1588 void
1589 mono_thread_info_describe (MonoThreadInfo *info, GString *text)
1591 mono_threads_platform_describe (info, text);
1594 void
1595 mono_thread_info_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
1597 mono_threads_platform_own_mutex (info, mutex_handle);
1600 void
1601 mono_thread_info_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
1603 mono_threads_platform_disown_mutex (info, mutex_handle);
1606 MonoThreadPriority
1607 mono_thread_info_get_priority (MonoThreadInfo *info)
1609 return mono_threads_platform_get_priority (info);
1612 gboolean
1613 mono_thread_info_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
1615 return mono_threads_platform_set_priority (info, priority);