[threads] Signal the w32handle later in the thread destruction process (#3652)
[mono-project.git] / mono / utils / mono-threads.c
blobf8c716b5038d941b9a74edbf0f0ab84e52be9450
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 inline void
264 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
266 if (retain != 0)
267 mono_hazard_pointer_clear (hp, 0);
268 if (retain != 1)
269 mono_hazard_pointer_clear (hp, 1);
270 if (retain != 2)
271 mono_hazard_pointer_clear (hp, 2);
275 If return non null Hazard Pointer 1 holds the return value.
277 MonoThreadInfo*
278 mono_thread_info_lookup (MonoNativeThreadId id)
280 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
282 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
283 mono_hazard_pointer_clear_all (hp, -1);
284 return NULL;
287 mono_hazard_pointer_clear_all (hp, 1);
288 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
291 static gboolean
292 mono_thread_info_insert (MonoThreadInfo *info)
294 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
296 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
297 mono_hazard_pointer_clear_all (hp, -1);
298 return FALSE;
301 mono_hazard_pointer_clear_all (hp, -1);
302 return TRUE;
305 static gboolean
306 mono_thread_info_remove (MonoThreadInfo *info)
308 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
309 gboolean res;
311 THREADS_DEBUG ("removing info %p\n", info);
312 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
313 mono_hazard_pointer_clear_all (hp, -1);
314 return res;
317 static void
318 free_thread_info (gpointer mem)
320 MonoThreadInfo *info = (MonoThreadInfo *) mem;
322 mono_os_sem_destroy (&info->resume_semaphore);
323 mono_threads_suspend_free (info);
325 g_free (info);
329 mono_thread_info_register_small_id (void)
331 int small_id = mono_thread_small_id_alloc ();
332 #ifdef HAVE_KW_THREAD
333 tls_small_id = small_id;
334 #else
335 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
336 #endif
337 return small_id;
340 static void*
341 register_thread (MonoThreadInfo *info, gpointer baseptr)
343 size_t stsize = 0;
344 guint8 *staddr = NULL;
345 int small_id = mono_thread_info_register_small_id ();
346 gboolean result;
347 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
348 info->small_id = small_id;
350 mono_os_sem_init (&info->resume_semaphore, 0);
352 /*set TLS early so SMR works */
353 mono_native_tls_set_value (thread_info_key, info);
355 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
357 if (threads_callbacks.thread_register) {
358 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
359 // g_warning ("thread registation failed\n");
360 mono_native_tls_set_value (thread_info_key, NULL);
361 g_free (info);
362 return NULL;
366 mono_thread_info_get_stack_bounds (&staddr, &stsize);
367 g_assert (staddr);
368 g_assert (stsize);
369 info->stack_start_limit = staddr;
370 info->stack_end = staddr + stsize;
372 info->stackdata = g_byte_array_new ();
374 mono_threads_platform_register (info);
375 mono_threads_suspend_register (info);
378 Transition it before taking any locks or publishing itself to reduce the chance
379 of others witnessing a detached thread.
380 We can reasonably expect that until this thread gets published, no other thread will
381 try to manipulate it.
383 mono_threads_transition_attach (info);
384 mono_thread_info_suspend_lock ();
385 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
386 result = mono_thread_info_insert (info);
387 g_assert (result);
388 mono_thread_info_suspend_unlock ();
389 return info;
392 static void
393 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
395 static void
396 unregister_thread (void *arg)
398 gpointer gc_unsafe_stackdata;
399 MonoThreadInfo *info;
400 int small_id;
401 gboolean result;
402 gpointer handle;
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));
420 * TLS destruction order is not reliable so small_id might be cleaned up
421 * before us.
423 #ifndef HAVE_KW_THREAD
424 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
425 #endif
427 /* we need to duplicate it, as the info->handle is going
428 * to be closed when unregistering from the platform */
429 handle = mono_threads_platform_duplicate_handle (info);
432 First perform the callback that requires no locks.
433 This callback has the potential of taking other locks, so we do it before.
434 After it completes, the thread remains functional.
436 if (threads_callbacks.thread_detach)
437 threads_callbacks.thread_detach (info);
439 mono_thread_info_suspend_lock_with_info (info);
442 Now perform the callback that must be done under locks.
443 This will render the thread useless and non-suspendable, so it must
444 be done while holding the suspend lock to give no other thread chance
445 to suspend it.
447 if (threads_callbacks.thread_unregister)
448 threads_callbacks.thread_unregister (info);
450 mono_threads_platform_unregister (info);
451 result = mono_thread_info_remove (info);
452 g_assert (result);
453 mono_threads_transition_detach (info);
455 mono_thread_info_suspend_unlock ();
457 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
459 /*now it's safe to free the thread info.*/
460 mono_thread_hazardous_try_free (info, free_thread_info);
461 /* Pump the HP queue */
462 mono_thread_hazardous_try_free_some ();
464 mono_thread_small_id_free (small_id);
466 /* Signal the w32handle. It can be done as late as here
467 * because w32handle does not access the current MonoThreadInfo,
468 * neither does it switch state to BLOCKING. */
469 mono_threads_platform_set_exited (handle);
471 mono_threads_platform_close_thread_handle (handle);
474 static void
475 thread_exited_dtor (void *arg)
477 #if defined(__MACH__)
479 * Since we use pthread dtors to clean up thread data, if a thread
480 * is attached to the runtime by another pthread dtor after our dtor
481 * has ran, it will never be detached, leading to various problems
482 * since the thread ids etc. will be reused while they are still in
483 * the threads hashtables etc.
484 * Dtors are called in a loop until all user tls entries are 0,
485 * but the loop has a maximum count (4), so if we set the tls
486 * variable every time, it will remain set when system tls dtors
487 * are ran. This allows mono_thread_info_is_exiting () to detect
488 * whenever the thread is exiting, even if it is executed from a
489 * system tls dtor (i.e. obj-c dealloc methods).
491 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
492 #endif
495 MonoThreadInfo*
496 mono_thread_info_current_unchecked (void)
498 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
502 MonoThreadInfo*
503 mono_thread_info_current (void)
505 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
506 if (info)
507 return info;
509 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
512 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
513 The way to distinguish between before, during and after cleanup is the following:
515 -If the TLS key is set, cleanup has not begun;
516 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
517 -If the thread is nowhere to be found, cleanup has finished.
519 We cannot function after cleanup since there's no way to ensure what will happen.
521 g_assert (info);
523 /*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 */
524 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
526 return info;
530 mono_thread_info_get_small_id (void)
532 #ifdef HAVE_KW_THREAD
533 return tls_small_id;
534 #else
535 gpointer val = mono_native_tls_get_value (small_id_key);
536 if (!val)
537 return -1;
538 return GPOINTER_TO_INT (val) - 1;
539 #endif
542 MonoLinkedListSet*
543 mono_thread_info_list_head (void)
545 return &thread_list;
549 * mono_threads_attach_tools_thread
551 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
553 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
554 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
555 * the managed heap.
557 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
558 * doing things like resolving backtraces in their background processing thread.
560 void
561 mono_threads_attach_tools_thread (void)
563 int dummy = 0;
564 MonoThreadInfo *info;
566 /* Must only be called once */
567 g_assert (!mono_native_tls_get_value (thread_info_key));
569 while (!mono_threads_inited) {
570 mono_thread_info_usleep (10);
573 info = mono_thread_info_attach (&dummy);
574 g_assert (info);
576 info->tools_thread = TRUE;
579 MonoThreadInfo*
580 mono_thread_info_attach (void *baseptr)
582 MonoThreadInfo *info;
583 if (!mono_threads_inited)
585 #ifdef HOST_WIN32
586 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
587 * thread is created before an embedding API user initialized Mono. */
588 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
589 return NULL;
590 #else
591 g_assert (mono_threads_inited);
592 #endif
594 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
595 if (!info) {
596 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
597 THREADS_DEBUG ("attaching %p\n", info);
598 if (!register_thread (info, baseptr))
599 return NULL;
600 } else if (threads_callbacks.thread_attach) {
601 threads_callbacks.thread_attach (info);
603 return info;
606 void
607 mono_thread_info_detach (void)
609 MonoThreadInfo *info;
610 if (!mono_threads_inited)
612 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
613 * is created before an embedding API user initialized Mono. */
614 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
615 return;
617 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
618 if (info) {
619 THREADS_DEBUG ("detaching %p\n", info);
620 unregister_thread (info);
621 mono_native_tls_set_value (thread_info_key, NULL);
626 * mono_thread_info_is_exiting:
628 * Return whenever the current thread is exiting, i.e. it is running pthread
629 * dtors.
631 gboolean
632 mono_thread_info_is_exiting (void)
634 #if defined(__MACH__)
635 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
636 return TRUE;
637 #endif
638 return FALSE;
641 #ifndef HOST_WIN32
642 static void
643 thread_info_key_dtor (void *arg)
645 /* Put the MonoThreadInfo back for the duration of the
646 * unregister code. In some circumstances the thread needs to
647 * take the GC lock which may block which requires a coop
648 * state transition. */
649 mono_native_tls_set_value (thread_info_key, arg);
650 unregister_thread (arg);
651 mono_native_tls_set_value (thread_info_key, NULL);
653 #endif
655 void
656 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
658 gboolean res;
659 threads_callbacks = *callbacks;
660 thread_info_size = info_size;
661 const char *sleepLimit;
662 #ifdef HOST_WIN32
663 res = mono_native_tls_alloc (&thread_info_key, NULL);
664 res = mono_native_tls_alloc (&thread_exited_key, NULL);
665 #else
666 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
667 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
668 #endif
670 g_assert (res);
672 #ifndef HAVE_KW_THREAD
673 res = mono_native_tls_alloc (&small_id_key, NULL);
674 #endif
675 g_assert (res);
677 unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
679 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
680 errno = 0;
681 long threshold = strtol(sleepLimit, NULL, 10);
682 if ((errno == 0) && (threshold >= 40)) {
683 sleepAbortDuration = threshold;
684 sleepWarnDuration = threshold / 20;
685 } else
686 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
689 mono_os_sem_init (&global_suspend_semaphore, 1);
690 mono_os_sem_init (&suspend_semaphore, 0);
692 mono_lls_init (&thread_list, NULL);
693 mono_thread_smr_init ();
694 mono_threads_platform_init ();
695 mono_threads_suspend_init ();
696 mono_threads_coop_init ();
697 mono_threads_abort_syscall_init ();
699 #if defined(__MACH__)
700 mono_mach_init (thread_info_key);
701 #endif
703 mono_threads_inited = TRUE;
705 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
708 void
709 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
711 runtime_callbacks = *callbacks;
714 MonoThreadInfoRuntimeCallbacks *
715 mono_threads_get_runtime_callbacks (void)
717 return &runtime_callbacks;
721 Signal that the current thread wants to be suspended.
722 This function can be called without holding the suspend lock held.
723 To finish suspending, call mono_suspend_check.
725 void
726 mono_thread_info_begin_self_suspend (void)
728 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
729 if (!info)
730 return;
732 THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
733 mono_threads_transition_request_self_suspension (info);
736 void
737 mono_thread_info_end_self_suspend (void)
739 MonoThreadInfo *info;
741 info = mono_thread_info_current ();
742 if (!info)
743 return;
744 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
746 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
748 /* commit the saved state and notify others if needed */
749 switch (mono_threads_transition_state_poll (info)) {
750 case SelfSuspendResumed:
751 return;
752 case SelfSuspendWait:
753 mono_thread_info_wait_for_resume (info);
754 break;
755 case SelfSuspendNotifyAndWait:
756 mono_threads_notify_initiator_of_suspend (info);
757 mono_thread_info_wait_for_resume (info);
758 mono_threads_notify_initiator_of_resume (info);
759 break;
763 static gboolean
764 mono_thread_info_core_resume (MonoThreadInfo *info)
766 gboolean res = FALSE;
768 switch (mono_threads_transition_request_resume (info)) {
769 case ResumeError:
770 res = FALSE;
771 break;
772 case ResumeOk:
773 res = TRUE;
774 break;
775 case ResumeInitSelfResume:
776 resume_self_suspended (info);
777 res = TRUE;
778 break;
779 case ResumeInitAsyncResume:
780 resume_async_suspended (info);
781 res = TRUE;
782 break;
783 case ResumeInitBlockingResume:
784 resume_blocking_suspended (info);
785 res = TRUE;
786 break;
789 return res;
792 gboolean
793 mono_thread_info_resume (MonoNativeThreadId tid)
795 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
796 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
797 MonoThreadInfo *info;
799 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
801 mono_thread_info_suspend_lock ();
803 info = mono_thread_info_lookup (tid); /*info on HP1*/
804 if (!info) {
805 result = FALSE;
806 goto cleanup;
809 result = mono_thread_info_core_resume (info);
811 //Wait for the pending resume to finish
812 mono_threads_wait_pending_operations ();
814 cleanup:
815 mono_thread_info_suspend_unlock ();
816 mono_hazard_pointer_clear (hp, 1);
817 return result;
820 gboolean
821 mono_thread_info_begin_suspend (MonoThreadInfo *info)
823 switch (mono_threads_transition_request_async_suspension (info)) {
824 case AsyncSuspendAlreadySuspended:
825 case AsyncSuspendBlocking:
826 return TRUE;
827 case AsyncSuspendWait:
828 mono_threads_add_to_pending_operation_set (info);
829 return TRUE;
830 case AsyncSuspendInitSuspend:
831 return begin_async_suspend (info, FALSE);
832 default:
833 g_assert_not_reached ();
837 gboolean
838 mono_thread_info_begin_resume (MonoThreadInfo *info)
840 return mono_thread_info_core_resume (info);
844 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
845 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
847 static gboolean
848 is_thread_in_critical_region (MonoThreadInfo *info)
850 MonoMethod *method;
851 MonoJitInfo *ji;
852 gpointer stack_start;
853 MonoThreadUnwindState *state;
855 /* Are we inside a system critical region? */
856 if (info->inside_critical_region)
857 return TRUE;
859 /* Are we inside a GC critical region? */
860 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
861 return TRUE;
864 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
865 state = mono_thread_info_get_suspend_state (info);
866 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
867 return FALSE;
869 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
870 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
871 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
872 return TRUE;
874 ji = mono_jit_info_table_find (
875 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
876 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
878 if (!ji)
879 return FALSE;
881 method = mono_jit_info_get_method (ji);
883 return threads_callbacks.mono_method_is_critical (method);
886 gboolean
887 mono_thread_info_in_critical_location (MonoThreadInfo *info)
889 return is_thread_in_critical_region (info);
893 The return value is only valid until a matching mono_thread_info_resume is called
895 static MonoThreadInfo*
896 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
898 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
899 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
900 if (!info)
901 return NULL;
903 switch (mono_threads_transition_request_async_suspension (info)) {
904 case AsyncSuspendAlreadySuspended:
905 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
906 return info;
907 case AsyncSuspendWait:
908 mono_threads_add_to_pending_operation_set (info);
909 break;
910 case AsyncSuspendInitSuspend:
911 if (!begin_async_suspend (info, interrupt_kernel)) {
912 mono_hazard_pointer_clear (hp, 1);
913 return NULL;
915 break;
916 case AsyncSuspendBlocking:
917 if (interrupt_kernel)
918 mono_threads_suspend_abort_syscall (info);
920 break;
921 default:
922 g_assert_not_reached ();
925 //Wait for the pending suspend to finish
926 mono_threads_wait_pending_operations ();
928 if (!check_async_suspend (info)) {
929 mono_thread_info_core_resume (info);
930 mono_threads_wait_pending_operations ();
931 mono_hazard_pointer_clear (hp, 1);
932 return NULL;
934 return info;
937 static MonoThreadInfo*
938 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
940 MonoThreadInfo *info = NULL;
941 int sleep_duration = 0;
942 for (;;) {
943 if (!(info = suspend_sync (id, interrupt_kernel))) {
944 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
945 return NULL;
948 /*WARNING: We now are in interrupt context until we resume the thread. */
949 if (!is_thread_in_critical_region (info))
950 break;
952 if (!mono_thread_info_core_resume (info)) {
953 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
954 return NULL;
956 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
958 /* Wait for the pending resume to finish */
959 mono_threads_wait_pending_operations ();
961 if (sleep_duration == 0)
962 mono_thread_info_yield ();
963 else
964 g_usleep (sleep_duration);
966 sleep_duration += 10;
968 return info;
971 void
972 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
974 int result;
975 MonoThreadInfo *info = NULL;
976 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
978 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
979 /*FIXME: unify this with self-suspend*/
980 g_assert (id != mono_native_thread_id_get ());
982 /* This can block during stw */
983 mono_thread_info_suspend_lock ();
984 mono_threads_begin_global_suspend ();
986 info = suspend_sync_nolock (id, interrupt_kernel);
987 if (!info)
988 goto done;
990 switch (result = callback (info, user_data)) {
991 case MonoResumeThread:
992 mono_hazard_pointer_set (hp, 1, info);
993 mono_thread_info_core_resume (info);
994 mono_threads_wait_pending_operations ();
995 break;
996 case KeepSuspended:
997 break;
998 default:
999 g_error ("Invalid suspend_and_run callback return value %d", result);
1002 done:
1003 mono_hazard_pointer_clear (hp, 1);
1004 mono_threads_end_global_suspend ();
1005 mono_thread_info_suspend_unlock ();
1009 Inject an assynchronous call into the target thread. The target thread must be suspended and
1010 only a single async call can be setup for a given suspend cycle.
1011 This async call must cause stack unwinding as the current implementation doesn't save enough state
1012 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1013 currently used only to deliver exceptions.
1015 void
1016 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1018 /* An async call can only be setup on an async suspended thread */
1019 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1020 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1021 g_assert (!info->async_target);
1022 info->async_target = target_func;
1023 /* This is not GC tracked */
1024 info->user_data = user_data;
1028 The suspend lock is held during any suspend in progress.
1029 A GC that has safepoints must take this lock as part of its
1030 STW to make sure no unsafe pending suspend is in progress.
1033 static void
1034 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1036 g_assert (info);
1037 g_assert (mono_thread_info_is_current (info));
1038 g_assert (mono_thread_info_is_live (info));
1040 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1042 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1043 g_assert (res != -1);
1045 MONO_EXIT_GC_SAFE_WITH_INFO;
1048 void
1049 mono_thread_info_suspend_lock (void)
1051 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1054 void
1055 mono_thread_info_suspend_unlock (void)
1057 mono_os_sem_post (&global_suspend_semaphore);
1061 * This is a very specific function whose only purpose is to
1062 * break a given thread from socket syscalls.
1064 * This only exists because linux won't fail a call to connect
1065 * if the underlying is closed.
1067 * TODO We should cleanup and unify this with the other syscall abort
1068 * facility.
1070 void
1071 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1073 MonoThreadHazardPointers *hp;
1074 MonoThreadInfo *info;
1076 if (tid == mono_native_thread_id_get () || !mono_threads_suspend_needs_abort_syscall ())
1077 return;
1079 hp = mono_hazard_pointer_get ();
1080 info = mono_thread_info_lookup (tid);
1081 if (!info)
1082 return;
1084 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1085 mono_hazard_pointer_clear (hp, 1);
1086 return;
1089 mono_thread_info_suspend_lock ();
1090 mono_threads_begin_global_suspend ();
1092 mono_threads_suspend_abort_syscall (info);
1093 mono_threads_wait_pending_operations ();
1095 mono_hazard_pointer_clear (hp, 1);
1097 mono_threads_end_global_suspend ();
1098 mono_thread_info_suspend_unlock ();
1101 gboolean
1102 mono_thread_info_unified_management_enabled (void)
1104 return unified_suspend_enabled;
1108 * mono_thread_info_set_is_async_context:
1110 * Set whenever the current thread is in an async context. Some runtime functions might behave
1111 * differently while in an async context in order to be async safe.
1113 void
1114 mono_thread_info_set_is_async_context (gboolean async_context)
1116 MonoThreadInfo *info = mono_thread_info_current ();
1118 if (info)
1119 info->is_async_context = async_context;
1122 gboolean
1123 mono_thread_info_is_async_context (void)
1125 MonoThreadInfo *info = mono_thread_info_current ();
1127 if (info)
1128 return info->is_async_context;
1129 else
1130 return FALSE;
1133 typedef struct {
1134 gint32 ref;
1135 MonoThreadStart start_routine;
1136 gpointer start_routine_arg;
1137 gint32 priority;
1138 MonoCoopSem registered;
1139 gpointer handle;
1140 } CreateThreadData;
1142 static gsize WINAPI
1143 inner_start_thread (gpointer data)
1145 CreateThreadData *thread_data;
1146 MonoThreadInfo *info;
1147 MonoThreadStart start_routine;
1148 gpointer start_routine_arg;
1149 guint32 start_routine_res;
1150 gsize dummy;
1152 thread_data = (CreateThreadData*) data;
1153 g_assert (thread_data);
1155 start_routine = thread_data->start_routine;
1156 start_routine_arg = thread_data->start_routine_arg;
1158 info = mono_thread_info_attach (&dummy);
1159 info->runtime_thread = TRUE;
1161 thread_data->handle = mono_thread_info_duplicate_handle (info);
1163 mono_coop_sem_post (&thread_data->registered);
1165 if (InterlockedDecrement (&thread_data->ref) == 0) {
1166 mono_coop_sem_destroy (&thread_data->registered);
1167 g_free (thread_data);
1170 /* thread_data is not valid anymore */
1171 thread_data = NULL;
1173 /* Run the actual main function of the thread */
1174 start_routine_res = start_routine (start_routine_arg);
1176 mono_threads_platform_exit (start_routine_res);
1178 g_assert_not_reached ();
1182 * mono_threads_create_thread:
1184 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1185 * Returns: a windows or io-layer handle for the thread.
1187 HANDLE
1188 mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize stack_size, MonoNativeThreadId *out_tid)
1190 CreateThreadData *thread_data;
1191 gint res;
1192 gpointer ret;
1194 thread_data = g_new0 (CreateThreadData, 1);
1195 thread_data->ref = 2;
1196 thread_data->start_routine = start;
1197 thread_data->start_routine_arg = arg;
1198 mono_coop_sem_init (&thread_data->registered, 0);
1200 res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) thread_data, stack_size, out_tid);
1201 if (res != 0) {
1202 /* ref is not going to be decremented in inner_start_thread */
1203 InterlockedDecrement (&thread_data->ref);
1204 ret = NULL;
1205 goto done;
1208 res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
1209 g_assert (res == 0);
1211 ret = thread_data->handle;
1212 g_assert (ret);
1214 done:
1215 if (InterlockedDecrement (&thread_data->ref) == 0) {
1216 mono_coop_sem_destroy (&thread_data->registered);
1217 g_free (thread_data);
1220 return ret;
1224 * mono_thread_info_get_stack_bounds:
1226 * Return the address and size of the current threads stack. Return NULL as the
1227 * stack address if the stack address cannot be determined.
1229 void
1230 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1232 guint8 *current = (guint8 *)&stsize;
1233 mono_threads_platform_get_stack_bounds (staddr, stsize);
1234 if (!*staddr)
1235 return;
1237 /* Sanity check the result */
1238 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1240 /* When running under emacs, sometimes staddr is not aligned to a page size */
1241 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1244 gboolean
1245 mono_thread_info_yield (void)
1247 return mono_threads_platform_yield ();
1249 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1250 static MonoCoopMutex sleep_mutex;
1251 static MonoCoopCond sleep_cond;
1253 static void
1254 sleep_initialize (void)
1256 mono_coop_mutex_init (&sleep_mutex);
1257 mono_coop_cond_init (&sleep_cond);
1260 static void
1261 sleep_interrupt (gpointer data)
1263 mono_coop_mutex_lock (&sleep_mutex);
1264 mono_coop_cond_broadcast (&sleep_cond);
1265 mono_coop_mutex_unlock (&sleep_mutex);
1268 static inline guint32
1269 sleep_interruptable (guint32 ms, gboolean *alerted)
1271 gint64 now, end;
1273 g_assert (INFINITE == G_MAXUINT32);
1275 g_assert (alerted);
1276 *alerted = FALSE;
1278 if (ms != INFINITE)
1279 end = mono_msec_ticks() + ms;
1281 mono_lazy_initialize (&sleep_init, sleep_initialize);
1283 mono_coop_mutex_lock (&sleep_mutex);
1285 for (;;) {
1286 if (ms != INFINITE) {
1287 now = mono_msec_ticks();
1288 if (now >= end)
1289 break;
1292 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1293 if (*alerted) {
1294 mono_coop_mutex_unlock (&sleep_mutex);
1295 return WAIT_IO_COMPLETION;
1298 if (ms != INFINITE)
1299 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1300 else
1301 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1303 mono_thread_info_uninstall_interrupt (alerted);
1304 if (*alerted) {
1305 mono_coop_mutex_unlock (&sleep_mutex);
1306 return WAIT_IO_COMPLETION;
1310 mono_coop_mutex_unlock (&sleep_mutex);
1312 return 0;
1315 gint
1316 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1318 if (ms == 0) {
1319 MonoThreadInfo *info;
1321 mono_thread_info_yield ();
1323 info = mono_thread_info_current ();
1324 if (info && mono_thread_info_is_interrupt_state (info))
1325 return WAIT_IO_COMPLETION;
1327 return 0;
1330 if (alerted)
1331 return sleep_interruptable (ms, alerted);
1333 MONO_ENTER_GC_SAFE;
1335 if (ms == INFINITE) {
1336 do {
1337 #ifdef HOST_WIN32
1338 Sleep (G_MAXUINT32);
1339 #else
1340 sleep (G_MAXUINT32);
1341 #endif
1342 } while (1);
1343 } else {
1344 int ret;
1345 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1346 struct timespec start, target;
1348 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1349 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1350 g_assert (ret == 0);
1352 target = start;
1353 target.tv_sec += ms / 1000;
1354 target.tv_nsec += (ms % 1000) * 1000000;
1355 if (target.tv_nsec > 999999999) {
1356 target.tv_nsec -= 999999999;
1357 target.tv_sec ++;
1360 do {
1361 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1362 } while (ret != 0);
1363 #elif HOST_WIN32
1364 Sleep (ms);
1365 #else
1366 struct timespec req, rem;
1368 req.tv_sec = ms / 1000;
1369 req.tv_nsec = (ms % 1000) * 1000000;
1371 do {
1372 memset (&rem, 0, sizeof (rem));
1373 ret = nanosleep (&req, &rem);
1374 } while (ret != 0);
1375 #endif /* __linux__ */
1378 MONO_EXIT_GC_SAFE;
1380 return 0;
1383 gint
1384 mono_thread_info_usleep (guint64 us)
1386 MONO_ENTER_GC_SAFE;
1387 g_usleep (us);
1388 MONO_EXIT_GC_SAFE;
1389 return 0;
1392 gpointer
1393 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1395 return ((MonoThreadInfo*)info)->tls [key];
1399 * mono_threads_info_tls_set:
1401 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1402 * values of TLS variables for threads other than the current thread.
1403 * This should only be used for infrequently changing TLS variables, and it should
1404 * be paired with setting the real TLS variable since this provides no GC tracking.
1406 void
1407 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1409 ((MonoThreadInfo*)info)->tls [key] = value;
1413 * mono_thread_info_exit:
1415 * Exit the current thread.
1416 * This function doesn't return.
1418 void
1419 mono_thread_info_exit (void)
1421 mono_threads_platform_exit (0);
1425 * mono_threads_open_thread_handle:
1427 * Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1428 * The handle need to be closed by calling CloseHandle () when it is no
1429 * longer needed.
1431 HANDLE
1432 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1434 return mono_threads_platform_open_thread_handle (handle, tid);
1437 void
1438 mono_threads_close_thread_handle (HANDLE handle)
1440 return mono_threads_platform_close_thread_handle (handle);
1443 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1445 struct _MonoThreadInfoInterruptToken {
1446 void (*callback) (gpointer data);
1447 gpointer data;
1451 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1453 * - @callback: must be able to be called from another thread and always cancel the wait
1454 * - @data: passed to the callback
1455 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1456 * if set to TRUE, it must mean that the thread is in interrupted state
1458 void
1459 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1461 MonoThreadInfo *info;
1462 MonoThreadInfoInterruptToken *previous_token, *token;
1464 g_assert (callback);
1466 g_assert (interrupted);
1467 *interrupted = FALSE;
1469 info = mono_thread_info_current ();
1470 g_assert (info);
1472 /* The memory of this token can be freed at 2 places:
1473 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1474 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1475 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1476 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1477 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1478 token->callback = callback;
1479 token->data = data;
1481 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1483 if (previous_token) {
1484 if (previous_token != INTERRUPT_STATE)
1485 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1487 g_free (token);
1489 *interrupted = TRUE;
1492 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1493 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1496 void
1497 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1499 MonoThreadInfo *info;
1500 MonoThreadInfoInterruptToken *previous_token;
1502 g_assert (interrupted);
1503 *interrupted = FALSE;
1505 info = mono_thread_info_current ();
1506 g_assert (info);
1508 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1510 /* only the installer can uninstall the token */
1511 g_assert (previous_token);
1513 if (previous_token == INTERRUPT_STATE) {
1514 /* if it is interrupted, then it is going to be freed in finish interrupt */
1515 *interrupted = TRUE;
1516 } else {
1517 g_free (previous_token);
1520 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1521 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1524 static MonoThreadInfoInterruptToken*
1525 set_interrupt_state (MonoThreadInfo *info)
1527 MonoThreadInfoInterruptToken *token, *previous_token;
1529 g_assert (info);
1531 /* Atomically obtain the token the thread is
1532 * waiting on, and change it to a flag value. */
1534 do {
1535 previous_token = info->interrupt_token;
1537 /* Already interrupted */
1538 if (previous_token == INTERRUPT_STATE) {
1539 token = NULL;
1540 break;
1543 token = previous_token;
1544 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1546 return token;
1550 * mono_thread_info_prepare_interrupt:
1552 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1553 * - if the thread calls one of the WaitFor functions, the function will return with
1554 * WAIT_IO_COMPLETION instead of waiting
1555 * - if the thread was waiting when this function was called, the wait will be broken
1557 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1558 * didn't receive the interrupt signal yet, in this case it should call the wait function
1559 * again. This essentially means that the target thread will busy wait until it is ready to
1560 * process the interruption.
1562 MonoThreadInfoInterruptToken*
1563 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1565 MonoThreadInfoInterruptToken *token;
1567 token = set_interrupt_state (info);
1569 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1570 mono_thread_info_get_tid (info), token);
1572 return token;
1575 void
1576 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1578 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1580 if (token == NULL)
1581 return;
1583 g_assert (token->callback);
1585 token->callback (token->data);
1587 g_free (token);
1590 void
1591 mono_thread_info_self_interrupt (void)
1593 MonoThreadInfo *info;
1594 MonoThreadInfoInterruptToken *token;
1596 info = mono_thread_info_current ();
1597 g_assert (info);
1599 token = set_interrupt_state (info);
1600 g_assert (!token);
1602 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1603 mono_thread_info_get_tid (info));
1606 /* Clear the interrupted flag of the current thread, set with
1607 * mono_thread_info_self_interrupt, so it can wait again */
1608 void
1609 mono_thread_info_clear_self_interrupt ()
1611 MonoThreadInfo *info;
1612 MonoThreadInfoInterruptToken *previous_token;
1614 info = mono_thread_info_current ();
1615 g_assert (info);
1617 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1618 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1620 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1623 gboolean
1624 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1626 g_assert (info);
1627 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1630 void
1631 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1633 g_assert (info);
1635 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1636 g_string_append_printf (text, "not waiting");
1637 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1638 g_string_append_printf (text, "interrupted state");
1639 else
1640 g_string_append_printf (text, "waiting");
1643 gboolean
1644 mono_thread_info_is_current (MonoThreadInfo *info)
1646 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1649 void
1650 mono_thread_info_set_exited (THREAD_INFO_TYPE *info)
1652 g_assert (mono_thread_info_is_current (info));
1654 g_assert (info->handle);
1655 mono_threads_platform_set_exited (info->handle);
1658 gpointer
1659 mono_thread_info_duplicate_handle (MonoThreadInfo *info)
1661 g_assert (mono_thread_info_is_current (info));
1662 return mono_threads_platform_duplicate_handle (info);