[tools] Add nuget-hash-extractor tool to help produce the runtime ignored assemblies...
[mono-project.git] / mono / utils / mono-threads.c
blobe8893039fbf2eb2a89a828c4c4b7607f6a50ac44
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>
32 #include <mono/utils/mono-threads-debug.h>
33 #include <mono/utils/os-event.h>
35 #include <mono/io-layer/io-layer.h>
37 #include <errno.h>
39 #if defined(__MACH__)
40 #include <mono/utils/mach-support.h>
41 #endif
44 Mutex that makes sure only a single thread can be suspending others.
45 Suspend is a very racy operation since it requires restarting until
46 the target thread is not on an unsafe region.
48 We could implement this using critical regions, but would be much much
49 harder for an operation that is hardly performance critical.
51 The GC has to acquire this lock before starting a STW to make sure
52 a runtime suspend won't make it wronly see a thread in a safepoint
53 when it is in fact not.
55 This has to be a naked locking primitive, and not a coop aware one, as
56 it needs to be usable when destroying thread_info_key, the TLS key for
57 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
58 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
59 to an assertion error. We then simply switch state manually in
60 mono_thread_info_suspend_lock_with_info.
62 static MonoSemType global_suspend_semaphore;
64 static size_t thread_info_size;
65 static MonoThreadInfoCallbacks threads_callbacks;
66 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
67 static MonoNativeTlsKey thread_info_key, thread_exited_key;
68 #ifdef HAVE_KW_THREAD
69 static __thread guint32 tls_small_id;
70 #else
71 static MonoNativeTlsKey small_id_key;
72 #endif
73 static MonoLinkedListSet thread_list;
74 static gboolean mono_threads_inited = FALSE;
76 static MonoSemType suspend_semaphore;
77 static size_t pending_suspends;
79 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
81 /*warn at 50 ms*/
82 #define SLEEP_DURATION_BEFORE_WARNING (50)
83 /*never aborts */
84 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
86 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
87 sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
89 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
91 void
92 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
94 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
95 InterlockedIncrement (&abort_posts);
96 mono_os_sem_post (&suspend_semaphore);
99 void
100 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
102 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
103 InterlockedIncrement (&suspend_posts);
104 mono_os_sem_post (&suspend_semaphore);
107 void
108 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
110 THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
111 InterlockedIncrement (&resume_posts);
112 mono_os_sem_post (&suspend_semaphore);
115 static gboolean
116 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
118 if (mono_threads_is_coop_enabled ()) {
119 /* There's nothing else to do after we async request the thread to suspend */
120 mono_threads_add_to_pending_operation_set (info);
121 return TRUE;
124 return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
127 static gboolean
128 check_async_suspend (MonoThreadInfo *info)
130 if (mono_threads_is_coop_enabled ()) {
131 /* Async suspend can't async fail on coop */
132 return TRUE;
135 return mono_threads_suspend_check_suspend_result (info);
138 static void
139 resume_async_suspended (MonoThreadInfo *info)
141 if (mono_threads_is_coop_enabled ())
142 g_assert_not_reached ();
144 g_assert (mono_threads_suspend_begin_async_resume (info));
147 static void
148 resume_self_suspended (MonoThreadInfo* info)
150 THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
151 mono_os_sem_post (&info->resume_semaphore);
154 void
155 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
157 int res;
158 THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
159 res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
160 g_assert (res != -1);
163 static void
164 resume_blocking_suspended (MonoThreadInfo* info)
166 THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
167 mono_os_sem_post (&info->resume_semaphore);
170 void
171 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
173 THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
174 ++pending_suspends;
175 InterlockedIncrement (&pending_ops);
178 void
179 mono_threads_begin_global_suspend (void)
181 size_t ps = pending_suspends;
182 if (G_UNLIKELY (ps != 0))
183 g_error ("pending_suspends = %d, but must be 0", ps);
184 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,
185 abort_posts, waits_done, pending_ops);
186 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
187 mono_threads_coop_begin_global_suspend ();
190 void
191 mono_threads_end_global_suspend (void)
193 size_t ps = pending_suspends;
194 if (G_UNLIKELY (ps != 0))
195 g_error ("pending_suspends = %d, but must be 0", ps);
196 THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
197 abort_posts, waits_done, pending_ops);
198 g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
199 mono_threads_coop_end_global_suspend ();
202 static void
203 dump_threads (void)
205 MonoThreadInfo *cur = mono_thread_info_current ();
207 MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
208 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
209 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
210 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
211 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
212 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
213 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
214 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
215 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
216 MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
218 FOREACH_THREAD_SAFE (info) {
219 #ifdef TARGET_MACH
220 char thread_name [256] = { 0 };
221 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
223 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" : "" );
224 #else
225 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" : "" );
226 #endif
227 } FOREACH_THREAD_SAFE_END
230 gboolean
231 mono_threads_wait_pending_operations (void)
233 int i;
234 int c = pending_suspends;
236 /* Wait threads to park */
237 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
238 if (pending_suspends) {
239 MonoStopwatch suspension_time;
240 mono_stopwatch_start (&suspension_time);
241 for (i = 0; i < pending_suspends; ++i) {
242 THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
243 InterlockedIncrement (&waits_done);
244 if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
245 continue;
246 mono_stopwatch_stop (&suspension_time);
248 dump_threads ();
250 MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
251 g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
253 mono_stopwatch_stop (&suspension_time);
254 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
258 pending_suspends = 0;
260 return c > 0;
264 //Thread initialization code
266 static inline void
267 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
269 if (retain != 0)
270 mono_hazard_pointer_clear (hp, 0);
271 if (retain != 1)
272 mono_hazard_pointer_clear (hp, 1);
273 if (retain != 2)
274 mono_hazard_pointer_clear (hp, 2);
278 If return non null Hazard Pointer 1 holds the return value.
280 MonoThreadInfo*
281 mono_thread_info_lookup (MonoNativeThreadId id)
283 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
285 if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
286 mono_hazard_pointer_clear_all (hp, -1);
287 return NULL;
290 mono_hazard_pointer_clear_all (hp, 1);
291 return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
294 static gboolean
295 mono_thread_info_insert (MonoThreadInfo *info)
297 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
299 if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
300 mono_hazard_pointer_clear_all (hp, -1);
301 return FALSE;
304 mono_hazard_pointer_clear_all (hp, -1);
305 return TRUE;
308 static gboolean
309 mono_thread_info_remove (MonoThreadInfo *info)
311 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
312 gboolean res;
314 THREADS_DEBUG ("removing info %p\n", info);
315 res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
316 mono_hazard_pointer_clear_all (hp, -1);
317 return res;
320 static void
321 free_thread_info (gpointer mem)
323 MonoThreadInfo *info = (MonoThreadInfo *) mem;
325 mono_os_sem_destroy (&info->resume_semaphore);
326 mono_threads_suspend_free (info);
328 g_free (info);
332 mono_thread_info_register_small_id (void)
334 int small_id = mono_thread_small_id_alloc ();
335 #ifdef HAVE_KW_THREAD
336 tls_small_id = small_id;
337 #else
338 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
339 #endif
340 return small_id;
343 static void
344 thread_handle_destroy (gpointer data)
346 MonoThreadHandle *thread_handle;
348 thread_handle = (MonoThreadHandle*) data;
350 mono_os_event_destroy (&thread_handle->event);
351 g_free (thread_handle);
354 static void*
355 register_thread (MonoThreadInfo *info, gpointer baseptr)
357 size_t stsize = 0;
358 guint8 *staddr = NULL;
359 int small_id = mono_thread_info_register_small_id ();
360 gboolean result;
361 mono_thread_info_set_tid (info, mono_native_thread_id_get ());
362 info->small_id = small_id;
364 info->handle = g_new0 (MonoThreadHandle, 1);
365 mono_refcount_init (info->handle, thread_handle_destroy);
366 mono_os_event_init (&info->handle->event, FALSE);
368 mono_os_sem_init (&info->resume_semaphore, 0);
370 /*set TLS early so SMR works */
371 mono_native_tls_set_value (thread_info_key, info);
373 THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
375 if (threads_callbacks.thread_register) {
376 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
377 // g_warning ("thread registation failed\n");
378 mono_native_tls_set_value (thread_info_key, NULL);
379 g_free (info);
380 return NULL;
384 mono_thread_info_get_stack_bounds (&staddr, &stsize);
385 g_assert (staddr);
386 g_assert (stsize);
387 info->stack_start_limit = staddr;
388 info->stack_end = staddr + stsize;
390 info->stackdata = g_byte_array_new ();
392 mono_threads_suspend_register (info);
395 Transition it before taking any locks or publishing itself to reduce the chance
396 of others witnessing a detached thread.
397 We can reasonably expect that until this thread gets published, no other thread will
398 try to manipulate it.
400 mono_threads_transition_attach (info);
401 mono_thread_info_suspend_lock ();
402 /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
403 result = mono_thread_info_insert (info);
404 g_assert (result);
405 mono_thread_info_suspend_unlock ();
406 return info;
409 static void
410 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
412 static void
413 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
415 static void
416 unregister_thread (void *arg)
418 gpointer gc_unsafe_stackdata;
419 MonoThreadInfo *info;
420 int small_id;
421 gboolean result;
422 gpointer handle;
424 info = (MonoThreadInfo *) arg;
425 g_assert (info);
426 g_assert (mono_thread_info_is_current (info));
427 g_assert (mono_thread_info_is_live (info));
429 /* Pump the HP queue while the thread is alive.*/
430 mono_thread_hazardous_try_free_some ();
432 small_id = info->small_id;
434 /* We only enter the GC unsafe region, as when exiting this function, the thread
435 * will be detached, and the current MonoThreadInfo* will be destroyed. */
436 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
438 THREADS_DEBUG ("unregistering info %p\n", info);
440 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
443 * TLS destruction order is not reliable so small_id might be cleaned up
444 * before us.
446 #ifndef HAVE_KW_THREAD
447 mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
448 #endif
450 /* we need to duplicate it, as the info->handle is going
451 * to be closed when unregistering from the platform */
452 handle = mono_threads_open_thread_handle (info->handle);
455 First perform the callback that requires no locks.
456 This callback has the potential of taking other locks, so we do it before.
457 After it completes, the thread remains functional.
459 if (threads_callbacks.thread_detach)
460 threads_callbacks.thread_detach (info);
462 mono_thread_info_suspend_lock_with_info (info);
465 Now perform the callback that must be done under locks.
466 This will render the thread useless and non-suspendable, so it must
467 be done while holding the suspend lock to give no other thread chance
468 to suspend it.
470 if (threads_callbacks.thread_unregister)
471 threads_callbacks.thread_unregister (info);
473 /* The thread is no longer active, so unref its handle */
474 mono_threads_close_thread_handle (info->handle);
475 info->handle = NULL;
477 result = mono_thread_info_remove (info);
478 g_assert (result);
479 mono_threads_transition_detach (info);
481 mono_thread_info_suspend_unlock ();
483 g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
485 /*now it's safe to free the thread info.*/
486 mono_thread_hazardous_try_free (info, free_thread_info);
488 mono_thread_small_id_free (small_id);
490 mono_threads_signal_thread_handle (handle);
492 mono_threads_close_thread_handle (handle);
495 static void
496 thread_exited_dtor (void *arg)
498 #if defined(__MACH__)
500 * Since we use pthread dtors to clean up thread data, if a thread
501 * is attached to the runtime by another pthread dtor after our dtor
502 * has ran, it will never be detached, leading to various problems
503 * since the thread ids etc. will be reused while they are still in
504 * the threads hashtables etc.
505 * Dtors are called in a loop until all user tls entries are 0,
506 * but the loop has a maximum count (4), so if we set the tls
507 * variable every time, it will remain set when system tls dtors
508 * are ran. This allows mono_thread_info_is_exiting () to detect
509 * whenever the thread is exiting, even if it is executed from a
510 * system tls dtor (i.e. obj-c dealloc methods).
512 mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
513 #endif
516 MonoThreadInfo*
517 mono_thread_info_current_unchecked (void)
519 return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
523 MonoThreadInfo*
524 mono_thread_info_current (void)
526 MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
527 if (info)
528 return info;
530 info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
533 We might be called during thread cleanup, but we cannot be called after cleanup as happened.
534 The way to distinguish between before, during and after cleanup is the following:
536 -If the TLS key is set, cleanup has not begun;
537 -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
538 -If the thread is nowhere to be found, cleanup has finished.
540 We cannot function after cleanup since there's no way to ensure what will happen.
542 g_assert (info);
544 /*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 */
545 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
547 return info;
551 mono_thread_info_get_small_id (void)
553 #ifdef HAVE_KW_THREAD
554 return tls_small_id;
555 #else
556 gpointer val = mono_native_tls_get_value (small_id_key);
557 if (!val)
558 return -1;
559 return GPOINTER_TO_INT (val) - 1;
560 #endif
563 MonoLinkedListSet*
564 mono_thread_info_list_head (void)
566 return &thread_list;
570 * mono_threads_attach_tools_thread
572 * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
574 * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
575 * not be counted as a regular thread for high order facilities such as executing managed code or accessing
576 * the managed heap.
578 * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
579 * doing things like resolving backtraces in their background processing thread.
581 void
582 mono_threads_attach_tools_thread (void)
584 int dummy = 0;
585 MonoThreadInfo *info;
587 /* Must only be called once */
588 g_assert (!mono_native_tls_get_value (thread_info_key));
590 while (!mono_threads_inited) {
591 mono_thread_info_usleep (10);
594 info = mono_thread_info_attach (&dummy);
595 g_assert (info);
597 info->tools_thread = TRUE;
600 MonoThreadInfo*
601 mono_thread_info_attach (void *baseptr)
603 MonoThreadInfo *info;
604 if (!mono_threads_inited)
606 #ifdef HOST_WIN32
607 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
608 * thread is created before an embedding API user initialized Mono. */
609 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
610 return NULL;
611 #else
612 g_assert (mono_threads_inited);
613 #endif
615 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
616 if (!info) {
617 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
618 THREADS_DEBUG ("attaching %p\n", info);
619 if (!register_thread (info, baseptr))
620 return NULL;
621 } else if (threads_callbacks.thread_attach) {
622 threads_callbacks.thread_attach (info);
624 return info;
627 void
628 mono_thread_info_detach (void)
630 MonoThreadInfo *info;
631 if (!mono_threads_inited)
633 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
634 * is created before an embedding API user initialized Mono. */
635 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
636 return;
638 info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
639 if (info) {
640 THREADS_DEBUG ("detaching %p\n", info);
641 unregister_thread (info);
642 mono_native_tls_set_value (thread_info_key, NULL);
647 * mono_thread_info_is_exiting:
649 * Return whenever the current thread is exiting, i.e. it is running pthread
650 * dtors.
652 gboolean
653 mono_thread_info_is_exiting (void)
655 #if defined(__MACH__)
656 if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
657 return TRUE;
658 #endif
659 return FALSE;
662 #ifndef HOST_WIN32
663 static void
664 thread_info_key_dtor (void *arg)
666 /* Put the MonoThreadInfo back for the duration of the
667 * unregister code. In some circumstances the thread needs to
668 * take the GC lock which may block which requires a coop
669 * state transition. */
670 mono_native_tls_set_value (thread_info_key, arg);
671 unregister_thread (arg);
672 mono_native_tls_set_value (thread_info_key, NULL);
674 #endif
676 void
677 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
679 gboolean res;
680 threads_callbacks = *callbacks;
681 thread_info_size = info_size;
682 const char *sleepLimit;
683 #ifdef HOST_WIN32
684 res = mono_native_tls_alloc (&thread_info_key, NULL);
685 res = mono_native_tls_alloc (&thread_exited_key, NULL);
686 #else
687 res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
688 res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
689 #endif
691 g_assert (res);
693 #ifndef HAVE_KW_THREAD
694 res = mono_native_tls_alloc (&small_id_key, NULL);
695 #endif
696 g_assert (res);
698 if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
699 errno = 0;
700 long threshold = strtol(sleepLimit, NULL, 10);
701 if ((errno == 0) && (threshold >= 40)) {
702 sleepAbortDuration = threshold;
703 sleepWarnDuration = threshold / 20;
704 } else
705 g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
708 mono_os_sem_init (&global_suspend_semaphore, 1);
709 mono_os_sem_init (&suspend_semaphore, 0);
711 mono_lls_init (&thread_list, NULL);
712 mono_thread_smr_init ();
713 mono_threads_suspend_init ();
714 mono_threads_coop_init ();
715 mono_threads_platform_init ();
717 #if defined(__MACH__)
718 mono_mach_init (thread_info_key);
719 #endif
721 mono_threads_inited = TRUE;
723 g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
726 void
727 mono_threads_signals_init (void)
729 mono_threads_suspend_init_signals ();
732 void
733 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
735 runtime_callbacks = *callbacks;
738 MonoThreadInfoRuntimeCallbacks *
739 mono_threads_get_runtime_callbacks (void)
741 return &runtime_callbacks;
744 static gboolean
745 mono_thread_info_core_resume (MonoThreadInfo *info)
747 gboolean res = FALSE;
749 switch (mono_threads_transition_request_resume (info)) {
750 case ResumeError:
751 res = FALSE;
752 break;
753 case ResumeOk:
754 res = TRUE;
755 break;
756 case ResumeInitSelfResume:
757 resume_self_suspended (info);
758 res = TRUE;
759 break;
760 case ResumeInitAsyncResume:
761 resume_async_suspended (info);
762 res = TRUE;
763 break;
764 case ResumeInitBlockingResume:
765 resume_blocking_suspended (info);
766 res = TRUE;
767 break;
770 return res;
773 gboolean
774 mono_thread_info_resume (MonoNativeThreadId tid)
776 gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
777 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
778 MonoThreadInfo *info;
780 THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
782 mono_thread_info_suspend_lock ();
784 info = mono_thread_info_lookup (tid); /*info on HP1*/
785 if (!info) {
786 result = FALSE;
787 goto cleanup;
790 result = mono_thread_info_core_resume (info);
792 //Wait for the pending resume to finish
793 mono_threads_wait_pending_operations ();
795 cleanup:
796 mono_thread_info_suspend_unlock ();
797 mono_hazard_pointer_clear (hp, 1);
798 return result;
801 gboolean
802 mono_thread_info_begin_suspend (MonoThreadInfo *info)
804 switch (mono_threads_transition_request_async_suspension (info)) {
805 case AsyncSuspendAlreadySuspended:
806 case AsyncSuspendBlocking:
807 return TRUE;
808 case AsyncSuspendWait:
809 mono_threads_add_to_pending_operation_set (info);
810 return TRUE;
811 case AsyncSuspendInitSuspend:
812 return begin_async_suspend (info, FALSE);
813 default:
814 g_assert_not_reached ();
818 gboolean
819 mono_thread_info_begin_resume (MonoThreadInfo *info)
821 return mono_thread_info_core_resume (info);
825 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
826 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
828 static gboolean
829 is_thread_in_critical_region (MonoThreadInfo *info)
831 MonoMethod *method;
832 MonoJitInfo *ji;
833 gpointer stack_start;
834 MonoThreadUnwindState *state;
836 if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
837 return TRUE;
839 /* Are we inside a system critical region? */
840 if (info->inside_critical_region)
841 return TRUE;
843 /* Are we inside a GC critical region? */
844 if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
845 return TRUE;
848 /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
849 state = mono_thread_info_get_suspend_state (info);
850 if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
851 return FALSE;
853 stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
854 /* altstack signal handler, sgen can't handle them, so we treat them as critical */
855 if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
856 return TRUE;
858 if (threads_callbacks.ip_in_critical_region)
859 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
861 ji = mono_jit_info_table_find (
862 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
863 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
865 if (!ji)
866 return FALSE;
868 method = mono_jit_info_get_method (ji);
870 return threads_callbacks.mono_method_is_critical (method);
873 gboolean
874 mono_thread_info_in_critical_location (MonoThreadInfo *info)
876 return is_thread_in_critical_region (info);
880 The return value is only valid until a matching mono_thread_info_resume is called
882 static MonoThreadInfo*
883 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
885 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
886 MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
887 if (!info)
888 return NULL;
890 switch (mono_threads_transition_request_async_suspension (info)) {
891 case AsyncSuspendAlreadySuspended:
892 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
893 return info;
894 case AsyncSuspendWait:
895 mono_threads_add_to_pending_operation_set (info);
896 break;
897 case AsyncSuspendInitSuspend:
898 if (!begin_async_suspend (info, interrupt_kernel)) {
899 mono_hazard_pointer_clear (hp, 1);
900 return NULL;
902 break;
903 case AsyncSuspendBlocking:
904 if (interrupt_kernel && mono_threads_suspend_needs_abort_syscall ())
905 mono_threads_suspend_abort_syscall (info);
907 break;
908 default:
909 g_assert_not_reached ();
912 //Wait for the pending suspend to finish
913 mono_threads_wait_pending_operations ();
915 if (!check_async_suspend (info)) {
916 mono_thread_info_core_resume (info);
917 mono_threads_wait_pending_operations ();
918 mono_hazard_pointer_clear (hp, 1);
919 return NULL;
921 return info;
924 static MonoThreadInfo*
925 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
927 MonoThreadInfo *info = NULL;
928 int sleep_duration = 0;
929 for (;;) {
930 if (!(info = suspend_sync (id, interrupt_kernel))) {
931 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
932 return NULL;
935 /*WARNING: We now are in interrupt context until we resume the thread. */
936 if (!is_thread_in_critical_region (info))
937 break;
939 if (!mono_thread_info_core_resume (info)) {
940 mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
941 return NULL;
943 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
945 /* Wait for the pending resume to finish */
946 mono_threads_wait_pending_operations ();
948 if (sleep_duration == 0)
949 mono_thread_info_yield ();
950 else
951 g_usleep (sleep_duration);
953 sleep_duration += 10;
955 return info;
958 void
959 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
961 int result;
962 MonoThreadInfo *info = NULL;
963 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
965 THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
966 /*FIXME: unify this with self-suspend*/
967 g_assert (id != mono_native_thread_id_get ());
969 /* This can block during stw */
970 mono_thread_info_suspend_lock ();
971 mono_threads_begin_global_suspend ();
973 info = suspend_sync_nolock (id, interrupt_kernel);
974 if (!info)
975 goto done;
977 switch (result = callback (info, user_data)) {
978 case MonoResumeThread:
979 mono_hazard_pointer_set (hp, 1, info);
980 mono_thread_info_core_resume (info);
981 mono_threads_wait_pending_operations ();
982 break;
983 case KeepSuspended:
984 g_assert (!mono_threads_is_coop_enabled ());
985 break;
986 default:
987 g_error ("Invalid suspend_and_run callback return value %d", result);
990 done:
991 mono_hazard_pointer_clear (hp, 1);
992 mono_threads_end_global_suspend ();
993 mono_thread_info_suspend_unlock ();
997 Inject an assynchronous call into the target thread. The target thread must be suspended and
998 only a single async call can be setup for a given suspend cycle.
999 This async call must cause stack unwinding as the current implementation doesn't save enough state
1000 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1001 currently used only to deliver exceptions.
1003 void
1004 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1006 /* An async call can only be setup on an async suspended thread */
1007 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1008 /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1009 g_assert (!info->async_target);
1010 info->async_target = target_func;
1011 /* This is not GC tracked */
1012 info->user_data = user_data;
1016 The suspend lock is held during any suspend in progress.
1017 A GC that has safepoints must take this lock as part of its
1018 STW to make sure no unsafe pending suspend is in progress.
1021 static void
1022 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1024 g_assert (info);
1025 g_assert (mono_thread_info_is_current (info));
1026 g_assert (mono_thread_info_is_live (info));
1028 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1030 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1031 g_assert (res != -1);
1033 MONO_EXIT_GC_SAFE_WITH_INFO;
1036 void
1037 mono_thread_info_suspend_lock (void)
1039 mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1042 void
1043 mono_thread_info_suspend_unlock (void)
1045 mono_os_sem_post (&global_suspend_semaphore);
1049 * This is a very specific function whose only purpose is to
1050 * break a given thread from socket syscalls.
1052 * This only exists because linux won't fail a call to connect
1053 * if the underlying is closed.
1055 * TODO We should cleanup and unify this with the other syscall abort
1056 * facility.
1058 void
1059 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1061 MonoThreadHazardPointers *hp;
1062 MonoThreadInfo *info;
1064 if (tid == mono_native_thread_id_get () || !mono_threads_suspend_needs_abort_syscall ())
1065 return;
1067 hp = mono_hazard_pointer_get ();
1068 info = mono_thread_info_lookup (tid);
1069 if (!info)
1070 return;
1072 if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1073 mono_hazard_pointer_clear (hp, 1);
1074 return;
1077 mono_thread_info_suspend_lock ();
1078 mono_threads_begin_global_suspend ();
1080 mono_threads_suspend_abort_syscall (info);
1081 mono_threads_wait_pending_operations ();
1083 mono_hazard_pointer_clear (hp, 1);
1085 mono_threads_end_global_suspend ();
1086 mono_thread_info_suspend_unlock ();
1090 * mono_thread_info_set_is_async_context:
1092 * Set whenever the current thread is in an async context. Some runtime functions might behave
1093 * differently while in an async context in order to be async safe.
1095 void
1096 mono_thread_info_set_is_async_context (gboolean async_context)
1098 MonoThreadInfo *info = mono_thread_info_current ();
1100 if (info)
1101 info->is_async_context = async_context;
1104 gboolean
1105 mono_thread_info_is_async_context (void)
1107 MonoThreadInfo *info = mono_thread_info_current ();
1109 if (info)
1110 return info->is_async_context;
1111 else
1112 return FALSE;
1115 typedef struct {
1116 MonoRefCount ref;
1117 MonoThreadStart start_routine;
1118 gpointer start_routine_arg;
1119 MonoCoopSem registered;
1120 MonoThreadHandle *handle;
1121 } CreateThreadData;
1123 static void
1124 create_thread_data_destroy (gpointer data)
1126 CreateThreadData *thread_data;
1128 thread_data = (CreateThreadData*) data;
1130 mono_coop_sem_destroy (&thread_data->registered);
1131 g_free (thread_data);
1134 static gsize WINAPI
1135 inner_start_thread (gpointer data)
1137 CreateThreadData *thread_data;
1138 MonoThreadInfo *info;
1139 MonoThreadStart start_routine;
1140 gpointer start_routine_arg;
1141 gsize start_routine_res;
1142 gsize dummy;
1144 thread_data = (CreateThreadData*) data;
1145 g_assert (thread_data);
1147 start_routine = thread_data->start_routine;
1148 start_routine_arg = thread_data->start_routine_arg;
1150 info = mono_thread_info_attach (&dummy);
1151 info->runtime_thread = TRUE;
1153 thread_data->handle = mono_threads_open_thread_handle (info->handle);
1155 mono_coop_sem_post (&thread_data->registered);
1157 mono_refcount_dec (thread_data);
1159 /* thread_data is not valid anymore */
1160 thread_data = NULL;
1162 /* Run the actual main function of the thread */
1163 start_routine_res = start_routine (start_routine_arg);
1165 mono_thread_info_exit (start_routine_res);
1167 g_assert_not_reached ();
1171 * mono_threads_create_thread:
1173 * Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1174 * Returns: a windows or io-layer handle for the thread.
1176 MonoThreadHandle*
1177 mono_threads_create_thread (MonoThreadStart start, gpointer arg, gsize * const stack_size, MonoNativeThreadId *out_tid)
1179 CreateThreadData *thread_data;
1180 gint res;
1181 MonoThreadHandle *ret;
1183 thread_data = g_new0 (CreateThreadData, 1);
1184 mono_refcount_init (thread_data, create_thread_data_destroy);
1185 thread_data->start_routine = start;
1186 thread_data->start_routine_arg = arg;
1187 mono_coop_sem_init (&thread_data->registered, 0);
1189 res = mono_threads_platform_create_thread (inner_start_thread, (gpointer) mono_refcount_inc (thread_data), stack_size, out_tid);
1190 if (res != 0) {
1191 /* ref is not going to be decremented in inner_start_thread */
1192 mono_refcount_dec (thread_data);
1193 ret = NULL;
1194 goto done;
1197 res = mono_coop_sem_wait (&thread_data->registered, MONO_SEM_FLAGS_NONE);
1198 g_assert (res == 0);
1200 ret = thread_data->handle;
1201 g_assert (ret);
1203 done:
1204 mono_refcount_dec (thread_data);
1206 return ret;
1210 * mono_thread_info_get_stack_bounds:
1212 * Return the address and size of the current threads stack. Return NULL as the
1213 * stack address if the stack address cannot be determined.
1215 void
1216 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1218 guint8 *current = (guint8 *)&stsize;
1219 mono_threads_platform_get_stack_bounds (staddr, stsize);
1220 if (!*staddr)
1221 return;
1223 /* Sanity check the result */
1224 g_assert ((current > *staddr) && (current < *staddr + *stsize));
1226 /* When running under emacs, sometimes staddr is not aligned to a page size */
1227 *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1230 gboolean
1231 mono_thread_info_yield (void)
1233 return mono_threads_platform_yield ();
1235 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1236 static MonoCoopMutex sleep_mutex;
1237 static MonoCoopCond sleep_cond;
1239 static void
1240 sleep_initialize (void)
1242 mono_coop_mutex_init (&sleep_mutex);
1243 mono_coop_cond_init (&sleep_cond);
1246 static void
1247 sleep_interrupt (gpointer data)
1249 mono_coop_mutex_lock (&sleep_mutex);
1250 mono_coop_cond_broadcast (&sleep_cond);
1251 mono_coop_mutex_unlock (&sleep_mutex);
1254 static inline guint32
1255 sleep_interruptable (guint32 ms, gboolean *alerted)
1257 gint64 now, end;
1259 g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1261 g_assert (alerted);
1262 *alerted = FALSE;
1264 if (ms != MONO_INFINITE_WAIT)
1265 end = mono_msec_ticks() + ms;
1267 mono_lazy_initialize (&sleep_init, sleep_initialize);
1269 mono_coop_mutex_lock (&sleep_mutex);
1271 for (;;) {
1272 if (ms != MONO_INFINITE_WAIT) {
1273 now = mono_msec_ticks();
1274 if (now >= end)
1275 break;
1278 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1279 if (*alerted) {
1280 mono_coop_mutex_unlock (&sleep_mutex);
1281 return WAIT_IO_COMPLETION;
1284 if (ms != MONO_INFINITE_WAIT)
1285 mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1286 else
1287 mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1289 mono_thread_info_uninstall_interrupt (alerted);
1290 if (*alerted) {
1291 mono_coop_mutex_unlock (&sleep_mutex);
1292 return WAIT_IO_COMPLETION;
1296 mono_coop_mutex_unlock (&sleep_mutex);
1298 return 0;
1301 gint
1302 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1304 if (ms == 0) {
1305 MonoThreadInfo *info;
1307 mono_thread_info_yield ();
1309 info = mono_thread_info_current ();
1310 if (info && mono_thread_info_is_interrupt_state (info))
1311 return WAIT_IO_COMPLETION;
1313 return 0;
1316 if (alerted)
1317 return sleep_interruptable (ms, alerted);
1319 MONO_ENTER_GC_SAFE;
1321 if (ms == MONO_INFINITE_WAIT) {
1322 do {
1323 #ifdef HOST_WIN32
1324 Sleep (G_MAXUINT32);
1325 #else
1326 sleep (G_MAXUINT32);
1327 #endif
1328 } while (1);
1329 } else {
1330 int ret;
1331 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1332 struct timespec start, target;
1334 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1335 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1336 g_assert (ret == 0);
1338 target = start;
1339 target.tv_sec += ms / 1000;
1340 target.tv_nsec += (ms % 1000) * 1000000;
1341 if (target.tv_nsec > 999999999) {
1342 target.tv_nsec -= 999999999;
1343 target.tv_sec ++;
1346 do {
1347 ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1348 } while (ret != 0);
1349 #elif HOST_WIN32
1350 Sleep (ms);
1351 #else
1352 struct timespec req, rem;
1354 req.tv_sec = ms / 1000;
1355 req.tv_nsec = (ms % 1000) * 1000000;
1357 do {
1358 memset (&rem, 0, sizeof (rem));
1359 ret = nanosleep (&req, &rem);
1360 } while (ret != 0);
1361 #endif /* __linux__ */
1364 MONO_EXIT_GC_SAFE;
1366 return 0;
1369 gint
1370 mono_thread_info_usleep (guint64 us)
1372 MONO_ENTER_GC_SAFE;
1373 g_usleep (us);
1374 MONO_EXIT_GC_SAFE;
1375 return 0;
1378 gpointer
1379 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1381 return ((MonoThreadInfo*)info)->tls [key];
1385 * mono_threads_info_tls_set:
1387 * Set the TLS key to VALUE in the info structure. This can be used to obtain
1388 * values of TLS variables for threads other than the current thread.
1389 * This should only be used for infrequently changing TLS variables, and it should
1390 * be paired with setting the real TLS variable since this provides no GC tracking.
1392 void
1393 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1395 ((MonoThreadInfo*)info)->tls [key] = value;
1398 #if defined(__native_client__)
1399 void nacl_shutdown_gc_thread(void);
1400 #endif
1403 * mono_thread_info_exit:
1405 * Exit the current thread.
1406 * This function doesn't return.
1408 void
1409 mono_thread_info_exit (gsize exit_code)
1411 #if defined(__native_client__)
1412 nacl_shutdown_gc_thread();
1413 #endif
1415 mono_thread_info_detach ();
1417 mono_threads_platform_exit (0);
1421 * mono_threads_open_thread_handle:
1423 * Duplicate the handle. The handle needs to be closed by calling
1424 * mono_threads_close_thread_handle () when it is no longer needed.
1426 MonoThreadHandle*
1427 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1429 return mono_refcount_inc (thread_handle);
1432 void
1433 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1435 mono_refcount_dec (thread_handle);
1438 static void
1439 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1441 mono_os_event_set (&thread_handle->event);
1444 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1446 struct _MonoThreadInfoInterruptToken {
1447 void (*callback) (gpointer data);
1448 gpointer data;
1452 * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1454 * - @callback: must be able to be called from another thread and always cancel the wait
1455 * - @data: passed to the callback
1456 * - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1457 * if set to TRUE, it must mean that the thread is in interrupted state
1459 void
1460 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1462 MonoThreadInfo *info;
1463 MonoThreadInfoInterruptToken *previous_token, *token;
1465 g_assert (callback);
1467 g_assert (interrupted);
1468 *interrupted = FALSE;
1470 info = mono_thread_info_current ();
1471 g_assert (info);
1473 /* The memory of this token can be freed at 2 places:
1474 * - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1475 * by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1476 * - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1477 * functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1478 token = g_new0 (MonoThreadInfoInterruptToken, 1);
1479 token->callback = callback;
1480 token->data = data;
1482 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1484 if (previous_token) {
1485 if (previous_token != INTERRUPT_STATE)
1486 g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1488 g_free (token);
1490 *interrupted = TRUE;
1493 THREADS_INTERRUPT_DEBUG ("interrupt install tid %p token %p previous_token %p interrupted %s\n",
1494 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1497 void
1498 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1500 MonoThreadInfo *info;
1501 MonoThreadInfoInterruptToken *previous_token;
1503 g_assert (interrupted);
1504 *interrupted = FALSE;
1506 info = mono_thread_info_current ();
1507 g_assert (info);
1509 previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1511 /* only the installer can uninstall the token */
1512 g_assert (previous_token);
1514 if (previous_token == INTERRUPT_STATE) {
1515 /* if it is interrupted, then it is going to be freed in finish interrupt */
1516 *interrupted = TRUE;
1517 } else {
1518 g_free (previous_token);
1521 THREADS_INTERRUPT_DEBUG ("interrupt uninstall tid %p previous_token %p interrupted %s\n",
1522 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1525 static MonoThreadInfoInterruptToken*
1526 set_interrupt_state (MonoThreadInfo *info)
1528 MonoThreadInfoInterruptToken *token, *previous_token;
1530 g_assert (info);
1532 /* Atomically obtain the token the thread is
1533 * waiting on, and change it to a flag value. */
1535 do {
1536 previous_token = info->interrupt_token;
1538 /* Already interrupted */
1539 if (previous_token == INTERRUPT_STATE) {
1540 token = NULL;
1541 break;
1544 token = previous_token;
1545 } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1547 return token;
1551 * mono_thread_info_prepare_interrupt:
1553 * The state of the thread info interrupt token is set to 'interrupted' which means that :
1554 * - if the thread calls one of the WaitFor functions, the function will return with
1555 * WAIT_IO_COMPLETION instead of waiting
1556 * - if the thread was waiting when this function was called, the wait will be broken
1558 * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1559 * didn't receive the interrupt signal yet, in this case it should call the wait function
1560 * again. This essentially means that the target thread will busy wait until it is ready to
1561 * process the interruption.
1563 MonoThreadInfoInterruptToken*
1564 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1566 MonoThreadInfoInterruptToken *token;
1568 token = set_interrupt_state (info);
1570 THREADS_INTERRUPT_DEBUG ("interrupt prepare tid %p token %p\n",
1571 mono_thread_info_get_tid (info), token);
1573 return token;
1576 void
1577 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1579 THREADS_INTERRUPT_DEBUG ("interrupt finish token %p\n", token);
1581 if (token == NULL)
1582 return;
1584 g_assert (token->callback);
1586 token->callback (token->data);
1588 g_free (token);
1591 void
1592 mono_thread_info_self_interrupt (void)
1594 MonoThreadInfo *info;
1595 MonoThreadInfoInterruptToken *token;
1597 info = mono_thread_info_current ();
1598 g_assert (info);
1600 token = set_interrupt_state (info);
1601 g_assert (!token);
1603 THREADS_INTERRUPT_DEBUG ("interrupt self tid %p\n",
1604 mono_thread_info_get_tid (info));
1607 /* Clear the interrupted flag of the current thread, set with
1608 * mono_thread_info_self_interrupt, so it can wait again */
1609 void
1610 mono_thread_info_clear_self_interrupt ()
1612 MonoThreadInfo *info;
1613 MonoThreadInfoInterruptToken *previous_token;
1615 info = mono_thread_info_current ();
1616 g_assert (info);
1618 previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1619 g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1621 THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1624 gboolean
1625 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1627 g_assert (info);
1628 return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1631 void
1632 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1634 g_assert (info);
1636 if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1637 g_string_append_printf (text, "not waiting");
1638 else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1639 g_string_append_printf (text, "interrupted state");
1640 else
1641 g_string_append_printf (text, "waiting");
1644 gboolean
1645 mono_thread_info_is_current (MonoThreadInfo *info)
1647 return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1650 MonoThreadInfoWaitRet
1651 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1653 MonoOSEventWaitRet res;
1655 res = mono_os_event_wait_one (&thread_handle->event, timeout);
1656 if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1657 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1658 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1659 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1660 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1661 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1662 else
1663 g_error ("%s: unknown res value %d", __func__, res);
1666 MonoThreadInfoWaitRet
1667 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1669 MonoOSEventWaitRet res;
1670 MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1671 gint i;
1673 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1674 if (background_change_event)
1675 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1677 for (i = 0; i < nhandles; ++i)
1678 thread_events [i] = &thread_handles [i]->event;
1680 if (background_change_event)
1681 thread_events [nhandles ++] = background_change_event;
1683 res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout);
1684 if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1685 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1686 else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1687 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1688 else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1689 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1690 else
1691 g_error ("%s: unknown res value %d", __func__, res);