Merge pull request #2500 from ludovic-henry/monoerror-mono_string_alloc
[mono.git] / mono / utils / mono-threads.c
1 /*
2  * mono-threads.c: Low-level threading
3  *
4  * Author:
5  *      Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * Copyright 2011 Novell, Inc (http://www.novell.com)
8  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
9  */
10
11 #include <config.h>
12
13 /* enable pthread extensions */
14 #ifdef TARGET_MACH
15 #define _DARWIN_C_SOURCE
16 #endif
17
18 #include <mono/utils/mono-compiler.h>
19 #include <mono/utils/mono-os-semaphore.h>
20 #include <mono/utils/mono-threads.h>
21 #include <mono/utils/mono-tls.h>
22 #include <mono/utils/hazard-pointer.h>
23 #include <mono/utils/mono-memory-model.h>
24 #include <mono/utils/mono-mmap.h>
25 #include <mono/utils/atomic.h>
26 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mono-lazy-init.h>
28 #include <mono/utils/mono-coop-mutex.h>
29 #include <mono/utils/mono-coop-semaphore.h>
30
31 #include <errno.h>
32
33 #if defined(__MACH__)
34 #include <mono/utils/mach-support.h>
35 #endif
36
37 /*
38 Mutex that makes sure only a single thread can be suspending others.
39 Suspend is a very racy operation since it requires restarting until
40 the target thread is not on an unsafe region.
41
42 We could implement this using critical regions, but would be much much
43 harder for an operation that is hardly performance critical.
44
45 The GC has to acquire this lock before starting a STW to make sure
46 a runtime suspend won't make it wronly see a thread in a safepoint
47 when it is in fact not.
48 */
49 static MonoCoopSem global_suspend_semaphore;
50
51 static size_t thread_info_size;
52 static MonoThreadInfoCallbacks threads_callbacks;
53 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
54 static MonoNativeTlsKey thread_info_key, thread_exited_key;
55 #ifdef HAVE_KW_THREAD
56 static __thread guint32 tls_small_id MONO_TLS_FAST;
57 #else
58 static MonoNativeTlsKey small_id_key;
59 #endif
60 static MonoLinkedListSet thread_list;
61 static gboolean mono_threads_inited = FALSE;
62
63 static MonoSemType suspend_semaphore;
64 static size_t pending_suspends;
65 static gboolean unified_suspend_enabled;
66
67 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
68
69 /*warn at 50 ms*/
70 #define SLEEP_DURATION_BEFORE_WARNING (10)
71 /*abort at 1 sec*/
72 #define SLEEP_DURATION_BEFORE_ABORT 200
73
74 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
75
76 void
77 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
78 {
79         THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
80         InterlockedIncrement (&abort_posts);
81         mono_os_sem_post (&suspend_semaphore);
82 }
83
84 void
85 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
86 {
87         THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
88         InterlockedIncrement (&suspend_posts);
89         mono_os_sem_post (&suspend_semaphore);
90 }
91
92 void
93 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
94 {
95         THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
96         InterlockedIncrement (&resume_posts);
97         mono_os_sem_post (&suspend_semaphore);
98 }
99
100 static gboolean
101 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
102 {
103         if (mono_threads_is_coop_enabled ()) {
104                 /* There's nothing else to do after we async request the thread to suspend */
105                 mono_threads_add_to_pending_operation_set (info);
106                 return TRUE;
107         }
108
109         return mono_threads_core_begin_async_suspend (info, interrupt_kernel);
110 }
111
112 static gboolean
113 check_async_suspend (MonoThreadInfo *info)
114 {
115         if (mono_threads_is_coop_enabled ()) {
116                 /* Async suspend can't async fail on coop */
117                 return TRUE;
118         }
119
120         return mono_threads_core_check_suspend_result (info);
121 }
122
123 static void
124 resume_async_suspended (MonoThreadInfo *info)
125 {
126         if (mono_threads_is_coop_enabled ())
127                 g_assert_not_reached ();
128
129         g_assert (mono_threads_core_begin_async_resume (info));
130 }
131
132 static void
133 resume_self_suspended (MonoThreadInfo* info)
134 {
135         THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
136         mono_os_sem_post (&info->resume_semaphore);
137 }
138
139 void
140 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
141 {
142         int res;
143         THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
144         res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
145         g_assert (res != -1);
146 }
147
148 static void
149 resume_blocking_suspended (MonoThreadInfo* info)
150 {
151         THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
152         mono_os_sem_post (&info->resume_semaphore);
153 }
154
155 void
156 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
157 {
158         THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
159         ++pending_suspends;
160         InterlockedIncrement (&pending_ops);
161 }
162
163 void
164 mono_threads_begin_global_suspend (void)
165 {
166         size_t ps = pending_suspends;
167         if (G_UNLIKELY (ps != 0))
168                 g_error ("pending_suspends = %d, but must be 0", ps);
169         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,
170                 abort_posts, waits_done, pending_ops);
171         g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
172         mono_threads_coop_begin_global_suspend ();
173 }
174
175 void
176 mono_threads_end_global_suspend (void) 
177 {
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 ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\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_end_global_suspend ();
185 }
186
187 static void
188 dump_threads (void)
189 {
190         MonoThreadInfo *info;
191         MonoThreadInfo *cur = mono_thread_info_current ();
192
193         MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
194         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
195         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
196         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
197         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
198         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
199         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
200         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
201         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
202         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
203
204         FOREACH_THREAD_SAFE (info) {
205 #ifdef TARGET_MACH
206                 char thread_name [256] = { 0 };
207                 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
208
209                 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" : "" );
210 #else
211                 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" : "" );
212 #endif
213
214         } END_FOREACH_THREAD_SAFE
215 }
216
217 gboolean
218 mono_threads_wait_pending_operations (void)
219 {
220         int i;
221         int c = pending_suspends;
222
223         /* Wait threads to park */
224         THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
225         if (pending_suspends) {
226                 MonoStopwatch suspension_time;
227                 mono_stopwatch_start (&suspension_time);
228                 for (i = 0; i < pending_suspends; ++i) {
229                         THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
230                         InterlockedIncrement (&waits_done);
231                         if (!mono_os_sem_timedwait (&suspend_semaphore, SLEEP_DURATION_BEFORE_ABORT, MONO_SEM_FLAGS_NONE))
232                                 continue;
233                         mono_stopwatch_stop (&suspension_time);
234
235                         dump_threads ();
236
237                         MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
238                         g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), SLEEP_DURATION_BEFORE_ABORT);
239                 }
240                 mono_stopwatch_stop (&suspension_time);
241                 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
242
243         }
244
245         pending_suspends = 0;
246
247         return c > 0;
248 }
249
250
251 //Thread initialization code
252
253 static void mono_threads_unregister_current_thread (MonoThreadInfo *info);
254
255 static inline void
256 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
257 {
258         if (retain != 0)
259                 mono_hazard_pointer_clear (hp, 0);
260         if (retain != 1)
261                 mono_hazard_pointer_clear (hp, 1);
262         if (retain != 2)
263                 mono_hazard_pointer_clear (hp, 2);
264 }
265
266 /*
267 If return non null Hazard Pointer 1 holds the return value.
268 */
269 MonoThreadInfo*
270 mono_thread_info_lookup (MonoNativeThreadId id)
271 {
272                 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
273
274         if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
275                 mono_hazard_pointer_clear_all (hp, -1);
276                 return NULL;
277         } 
278
279         mono_hazard_pointer_clear_all (hp, 1);
280         return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
281 }
282
283 static gboolean
284 mono_thread_info_insert (MonoThreadInfo *info)
285 {
286         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
287
288         if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
289                 mono_hazard_pointer_clear_all (hp, -1);
290                 return FALSE;
291         } 
292
293         mono_hazard_pointer_clear_all (hp, -1);
294         return TRUE;
295 }
296
297 static gboolean
298 mono_thread_info_remove (MonoThreadInfo *info)
299 {
300         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
301         gboolean res;
302
303         THREADS_DEBUG ("removing info %p\n", info);
304         res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
305         mono_hazard_pointer_clear_all (hp, -1);
306         return res;
307 }
308
309 static void
310 free_thread_info (gpointer mem)
311 {
312         MonoThreadInfo *info = (MonoThreadInfo *) mem;
313
314         mono_os_sem_destroy (&info->resume_semaphore);
315         mono_threads_platform_free (info);
316
317         g_free (info);
318 }
319
320 int
321 mono_thread_info_register_small_id (void)
322 {
323         int small_id = mono_thread_small_id_alloc ();
324 #ifdef HAVE_KW_THREAD
325         tls_small_id = small_id;
326 #else
327         mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
328 #endif
329         return small_id;
330 }
331
332 static void*
333 register_thread (MonoThreadInfo *info, gpointer baseptr)
334 {
335         size_t stsize = 0;
336         guint8 *staddr = NULL;
337         int small_id = mono_thread_info_register_small_id ();
338         gboolean result;
339         mono_thread_info_set_tid (info, mono_native_thread_id_get ());
340         info->small_id = small_id;
341
342         mono_os_sem_init (&info->resume_semaphore, 0);
343
344         /*set TLS early so SMR works */
345         mono_native_tls_set_value (thread_info_key, info);
346
347         THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
348
349         if (threads_callbacks.thread_register) {
350                 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
351                         // g_warning ("thread registation failed\n");
352                         g_free (info);
353                         return NULL;
354                 }
355         }
356
357         mono_thread_info_get_stack_bounds (&staddr, &stsize);
358         g_assert (staddr);
359         g_assert (stsize);
360         info->stack_start_limit = staddr;
361         info->stack_end = staddr + stsize;
362
363         info->stackdata = g_byte_array_new ();
364
365         mono_threads_platform_register (info);
366
367         /*
368         Transition it before taking any locks or publishing itself to reduce the chance
369         of others witnessing a detached thread.
370         We can reasonably expect that until this thread gets published, no other thread will
371         try to manipulate it.
372         */
373         mono_threads_transition_attach (info);
374         mono_thread_info_suspend_lock ();
375         /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
376         result = mono_thread_info_insert (info);
377         g_assert (result);
378         mono_thread_info_suspend_unlock ();
379         return info;
380 }
381
382 static void
383 unregister_thread (void *arg)
384 {
385         MonoThreadInfo *info = (MonoThreadInfo *) arg;
386         int small_id = info->small_id;
387         g_assert (info);
388
389         THREADS_DEBUG ("unregistering info %p\n", info);
390
391         mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
392
393         mono_threads_core_unregister (info);
394
395         /*
396          * TLS destruction order is not reliable so small_id might be cleaned up
397          * before us.
398          */
399 #ifndef HAVE_KW_THREAD
400         mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
401 #endif
402
403         /*
404         First perform the callback that requires no locks.
405         This callback has the potential of taking other locks, so we do it before.
406         After it completes, the thread remains functional.
407         */
408         if (threads_callbacks.thread_detach)
409                 threads_callbacks.thread_detach (info);
410
411         mono_thread_info_suspend_lock ();
412
413         /*
414         Now perform the callback that must be done under locks.
415         This will render the thread useless and non-suspendable, so it must
416         be done while holding the suspend lock to give no other thread chance
417         to suspend it.
418         */
419         if (threads_callbacks.thread_unregister)
420                 threads_callbacks.thread_unregister (info);
421         mono_threads_unregister_current_thread (info);
422         mono_threads_transition_detach (info);
423
424         mono_thread_info_suspend_unlock ();
425
426         g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
427
428         /*now it's safe to free the thread info.*/
429         mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
430         mono_thread_small_id_free (small_id);
431 }
432
433 static void
434 thread_exited_dtor (void *arg)
435 {
436 #if defined(__MACH__)
437         /*
438          * Since we use pthread dtors to clean up thread data, if a thread
439          * is attached to the runtime by another pthread dtor after our dtor
440          * has ran, it will never be detached, leading to various problems
441          * since the thread ids etc. will be reused while they are still in
442          * the threads hashtables etc.
443          * Dtors are called in a loop until all user tls entries are 0,
444          * but the loop has a maximum count (4), so if we set the tls
445          * variable every time, it will remain set when system tls dtors
446          * are ran. This allows mono_thread_info_is_exiting () to detect
447          * whenever the thread is exiting, even if it is executed from a
448          * system tls dtor (i.e. obj-c dealloc methods).
449          */
450         mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
451 #endif
452 }
453
454 /**
455  * Removes the current thread from the thread list.
456  * This must be called from the thread unregister callback and nowhere else.
457  * The current thread must be passed as TLS might have already been cleaned up.
458 */
459 static void
460 mono_threads_unregister_current_thread (MonoThreadInfo *info)
461 {
462         gboolean result;
463         g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
464         result = mono_thread_info_remove (info);
465         g_assert (result);
466 }
467
468 MonoThreadInfo*
469 mono_thread_info_current_unchecked (void)
470 {
471         return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
472 }
473
474
475 MonoThreadInfo*
476 mono_thread_info_current (void)
477 {
478         MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
479         if (info)
480                 return info;
481
482         info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
483
484         /*
485         We might be called during thread cleanup, but we cannot be called after cleanup as happened.
486         The way to distinguish between before, during and after cleanup is the following:
487
488         -If the TLS key is set, cleanup has not begun;
489         -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
490         -If the thread is nowhere to be found, cleanup has finished.
491
492         We cannot function after cleanup since there's no way to ensure what will happen.
493         */
494         g_assert (info);
495
496         /*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 */
497         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
498
499         return info;
500 }
501
502 int
503 mono_thread_info_get_small_id (void)
504 {
505 #ifdef HAVE_KW_THREAD
506         return tls_small_id;
507 #else
508         gpointer val = mono_native_tls_get_value (small_id_key);
509         if (!val)
510                 return -1;
511         return GPOINTER_TO_INT (val) - 1;
512 #endif
513 }
514
515 MonoLinkedListSet*
516 mono_thread_info_list_head (void)
517 {
518         return &thread_list;
519 }
520
521 /**
522  * mono_threads_attach_tools_thread
523  *
524  * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
525  *
526  * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
527  * not be counted as a regular thread for high order facilities such as executing managed code or accessing
528  * the managed heap.
529  *
530  * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
531  * doing things like resolving backtraces in their background processing thread.
532  */
533 void
534 mono_threads_attach_tools_thread (void)
535 {
536         int dummy = 0;
537         MonoThreadInfo *info;
538
539         /* Must only be called once */
540         g_assert (!mono_native_tls_get_value (thread_info_key));
541         
542         while (!mono_threads_inited) { 
543                 mono_thread_info_usleep (10);
544         }
545
546         info = mono_thread_info_attach (&dummy);
547         g_assert (info);
548
549         info->tools_thread = TRUE;
550 }
551
552 MonoThreadInfo*
553 mono_thread_info_attach (void *baseptr)
554 {
555         MonoThreadInfo *info;
556         if (!mono_threads_inited)
557         {
558 #ifdef HOST_WIN32
559                 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
560                  * thread is created before an embedding API user initialized Mono. */
561                 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
562                 return NULL;
563 #else
564                 g_assert (mono_threads_inited);
565 #endif
566         }
567         info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
568         if (!info) {
569                 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
570                 THREADS_DEBUG ("attaching %p\n", info);
571                 if (!register_thread (info, baseptr))
572                         return NULL;
573         } else if (threads_callbacks.thread_attach) {
574                 threads_callbacks.thread_attach (info);
575         }
576         return info;
577 }
578
579 void
580 mono_thread_info_detach (void)
581 {
582         MonoThreadInfo *info;
583         if (!mono_threads_inited)
584         {
585                 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
586                  * is created before an embedding API user initialized Mono. */
587                 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
588                 return;
589         }
590         info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
591         if (info) {
592                 THREADS_DEBUG ("detaching %p\n", info);
593                 unregister_thread (info);
594                 mono_native_tls_set_value (thread_info_key, NULL);
595         }
596 }
597
598 /*
599  * mono_thread_info_is_exiting:
600  *
601  *   Return whenever the current thread is exiting, i.e. it is running pthread
602  * dtors.
603  */
604 gboolean
605 mono_thread_info_is_exiting (void)
606 {
607 #if defined(__MACH__)
608         if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
609                 return TRUE;
610 #endif
611         return FALSE;
612 }
613
614 void
615 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
616 {
617         gboolean res;
618         threads_callbacks = *callbacks;
619         thread_info_size = info_size;
620 #ifdef HOST_WIN32
621         res = mono_native_tls_alloc (&thread_info_key, NULL);
622         res = mono_native_tls_alloc (&thread_exited_key, NULL);
623 #else
624         res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
625         res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
626 #endif
627
628         g_assert (res);
629
630 #ifndef HAVE_KW_THREAD
631         res = mono_native_tls_alloc (&small_id_key, NULL);
632 #endif
633         g_assert (res);
634
635         unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled ();
636
637         mono_coop_sem_init (&global_suspend_semaphore, 1);
638         mono_os_sem_init (&suspend_semaphore, 0);
639
640         mono_lls_init (&thread_list, NULL);
641         mono_thread_smr_init ();
642         mono_threads_init_platform ();
643         mono_threads_init_coop ();
644         mono_threads_init_abort_syscall ();
645
646 #if defined(__MACH__)
647         mono_mach_init (thread_info_key);
648 #endif
649
650         mono_threads_inited = TRUE;
651
652         g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
653 }
654
655 void
656 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
657 {
658         runtime_callbacks = *callbacks;
659 }
660
661 MonoThreadInfoRuntimeCallbacks *
662 mono_threads_get_runtime_callbacks (void)
663 {
664         return &runtime_callbacks;
665 }
666
667 /*
668 Signal that the current thread wants to be suspended.
669 This function can be called without holding the suspend lock held.
670 To finish suspending, call mono_suspend_check.
671 */
672 void
673 mono_thread_info_begin_self_suspend (void)
674 {
675         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
676         if (!info)
677                 return;
678
679         THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
680         mono_threads_transition_request_self_suspension (info);
681 }
682
683 void
684 mono_thread_info_end_self_suspend (void)
685 {
686         MonoThreadInfo *info;
687
688         info = mono_thread_info_current ();
689         if (!info)
690                 return;
691         THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", info);
692
693         mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
694
695         /* commit the saved state and notify others if needed */
696         switch (mono_threads_transition_state_poll (info)) {
697         case SelfSuspendResumed:
698                 return;
699         case SelfSuspendWait:
700                 mono_thread_info_wait_for_resume (info);
701                 break;
702         case SelfSuspendNotifyAndWait:
703                 mono_threads_notify_initiator_of_suspend (info);
704                 mono_thread_info_wait_for_resume (info);
705                 mono_threads_notify_initiator_of_resume (info);
706                 break;
707         }
708 }
709
710 static gboolean
711 mono_thread_info_core_resume (MonoThreadInfo *info)
712 {
713         gboolean res = FALSE;
714         if (info->create_suspended) {
715                 MonoNativeThreadId tid = mono_thread_info_get_tid (info);
716                 /* Have to special case this, as the normal suspend/resume pair are racy, they don't work if he resume is received before the suspend */
717                 info->create_suspended = FALSE;
718                 mono_threads_core_resume_created (info, tid);
719                 return TRUE;
720         }
721
722         switch (mono_threads_transition_request_resume (info)) {
723         case ResumeError:
724                 res = FALSE;
725                 break;
726         case ResumeOk:
727                 res = TRUE;
728                 break;
729         case ResumeInitSelfResume:
730                 resume_self_suspended (info);
731                 res = TRUE;
732                 break;
733         case ResumeInitAsyncResume:
734                 resume_async_suspended (info);
735                 res = TRUE;
736                 break;
737         case ResumeInitBlockingResume:
738                 resume_blocking_suspended (info);
739                 res = TRUE;
740                 break;
741         }
742
743         return res;
744 }
745
746 gboolean
747 mono_thread_info_resume (MonoNativeThreadId tid)
748 {
749         gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
750         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
751         MonoThreadInfo *info;
752
753         THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
754
755         mono_thread_info_suspend_lock ();
756
757         info = mono_thread_info_lookup (tid); /*info on HP1*/
758         if (!info) {
759                 result = FALSE;
760                 goto cleanup;
761         }
762
763         result = mono_thread_info_core_resume (info);
764
765         //Wait for the pending resume to finish
766         mono_threads_wait_pending_operations ();
767
768 cleanup:
769         mono_thread_info_suspend_unlock ();
770         mono_hazard_pointer_clear (hp, 1);
771         return result;
772 }
773
774 gboolean
775 mono_thread_info_begin_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
776 {
777         switch (mono_threads_transition_request_async_suspension (info)) {
778         case AsyncSuspendAlreadySuspended:
779                 return TRUE;
780         case AsyncSuspendWait:
781                 mono_threads_add_to_pending_operation_set (info);
782                 return TRUE;
783         case AsyncSuspendInitSuspend:
784                 return begin_async_suspend (info, interrupt_kernel);
785         default:
786                 g_assert_not_reached ();
787         }
788 }
789
790 gboolean
791 mono_thread_info_begin_resume (MonoThreadInfo *info)
792 {
793         return mono_thread_info_core_resume (info);
794 }
795
796 gboolean
797 mono_thread_info_check_suspend_result (MonoThreadInfo *info)
798 {
799         return check_async_suspend (info);
800 }
801
802 /*
803 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
804 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
805 */
806 static gboolean
807 is_thread_in_critical_region (MonoThreadInfo *info)
808 {
809         MonoMethod *method;
810         MonoJitInfo *ji;
811         gpointer stack_start;
812         MonoThreadUnwindState *state;
813
814         /* Are we inside a system critical region? */
815         if (info->inside_critical_region)
816                 return TRUE;
817
818         /* Are we inside a GC critical region? */
819         if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
820                 return TRUE;
821         }
822
823         /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
824         state = mono_thread_info_get_suspend_state (info);
825         if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
826                 return FALSE;
827
828         stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
829         /* altstack signal handler, sgen can't handle them, so we treat them as critical */
830         if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
831                 return TRUE;
832
833         ji = mono_jit_info_table_find (
834                 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
835                 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
836
837         if (!ji)
838                 return FALSE;
839
840         method = mono_jit_info_get_method (ji);
841
842         return threads_callbacks.mono_method_is_critical (method);
843 }
844
845 gboolean
846 mono_thread_info_in_critical_location (MonoThreadInfo *info)
847 {
848         return is_thread_in_critical_region (info);
849 }
850
851 /*
852 The return value is only valid until a matching mono_thread_info_resume is called
853 */
854 static MonoThreadInfo*
855 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
856 {
857         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
858         MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
859         if (!info)
860                 return NULL;
861
862         switch (mono_threads_transition_request_async_suspension (info)) {
863         case AsyncSuspendAlreadySuspended:
864                 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
865                 return info;
866         case AsyncSuspendWait:
867                 mono_threads_add_to_pending_operation_set (info);
868                 break;
869         case AsyncSuspendInitSuspend:
870                 if (!begin_async_suspend (info, interrupt_kernel)) {
871                         mono_hazard_pointer_clear (hp, 1);
872                         return NULL;
873                 }
874         }
875
876         //Wait for the pending suspend to finish
877         mono_threads_wait_pending_operations ();
878
879         if (!check_async_suspend (info)) {
880                 mono_hazard_pointer_clear (hp, 1);
881                 return NULL;
882         }
883         return info;
884 }
885
886 static MonoThreadInfo*
887 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
888 {
889         MonoThreadInfo *info = NULL;
890         int sleep_duration = 0;
891         for (;;) {
892                 if (!(info = suspend_sync (id, interrupt_kernel))) {
893                         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
894                         return NULL;
895                 }
896
897                 /*WARNING: We now are in interrupt context until we resume the thread. */
898                 if (!is_thread_in_critical_region (info))
899                         break;
900
901                 if (!mono_thread_info_core_resume (info)) {
902                         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
903                         return NULL;
904                 }
905                 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
906
907                 /* Wait for the pending resume to finish */
908                 mono_threads_wait_pending_operations ();
909
910                 if (sleep_duration == 0)
911                         mono_thread_info_yield ();
912                 else
913                         g_usleep (sleep_duration);
914
915                 sleep_duration += 10;
916         }
917         return info;
918 }
919
920 void
921 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
922 {
923         int result;
924         MonoThreadInfo *info = NULL;
925         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
926
927         THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
928         /*FIXME: unify this with self-suspend*/
929         g_assert (id != mono_native_thread_id_get ());
930
931         /* This can block during stw */
932         mono_thread_info_suspend_lock ();
933         mono_threads_begin_global_suspend ();
934
935         info = suspend_sync_nolock (id, interrupt_kernel);
936         if (!info)
937                 goto done;
938
939         switch (result = callback (info, user_data)) {
940         case MonoResumeThread:
941                 mono_hazard_pointer_set (hp, 1, info);
942                 mono_thread_info_core_resume (info);
943                 mono_threads_wait_pending_operations ();
944                 break;
945         case KeepSuspended:
946                 break;
947         default:
948                 g_error ("Invalid suspend_and_run callback return value %d", result);
949         }
950
951 done:
952         mono_hazard_pointer_clear (hp, 1);
953         mono_threads_end_global_suspend ();
954         mono_thread_info_suspend_unlock ();
955 }
956
957 /**
958 Inject an assynchronous call into the target thread. The target thread must be suspended and
959 only a single async call can be setup for a given suspend cycle.
960 This async call must cause stack unwinding as the current implementation doesn't save enough state
961 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
962 currently used only to deliver exceptions.
963 */
964 void
965 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
966 {
967         /* An async call can only be setup on an async suspended thread */
968         g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
969         /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
970         g_assert (!info->async_target);
971         info->async_target = target_func;
972         /* This is not GC tracked */
973         info->user_data = user_data;
974 }
975
976 /*
977 The suspend lock is held during any suspend in progress.
978 A GC that has safepoints must take this lock as part of its
979 STW to make sure no unsafe pending suspend is in progress.   
980 */
981 void
982 mono_thread_info_suspend_lock (void)
983 {
984         int res = mono_coop_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
985         g_assert (res != -1);
986 }
987
988 void
989 mono_thread_info_suspend_unlock (void)
990 {
991         mono_coop_sem_post (&global_suspend_semaphore);
992 }
993
994 /*
995  * This is a very specific function whose only purpose is to
996  * break a given thread from socket syscalls.
997  *
998  * This only exists because linux won't fail a call to connect
999  * if the underlying is closed.
1000  *
1001  * TODO We should cleanup and unify this with the other syscall abort
1002  * facility.
1003  */
1004 void
1005 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1006 {
1007         MonoThreadHazardPointers *hp;
1008         MonoThreadInfo *info;
1009
1010         if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1011                 return;
1012
1013         hp = mono_hazard_pointer_get ();
1014         info = mono_thread_info_lookup (tid);
1015         if (!info)
1016                 return;
1017
1018         if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1019                 mono_hazard_pointer_clear (hp, 1);
1020                 return;
1021         }
1022
1023         mono_thread_info_suspend_lock ();
1024         mono_threads_begin_global_suspend ();
1025
1026         mono_threads_core_abort_syscall (info);
1027         mono_threads_wait_pending_operations ();
1028
1029         mono_hazard_pointer_clear (hp, 1);
1030
1031         mono_threads_end_global_suspend ();
1032         mono_thread_info_suspend_unlock ();
1033 }
1034
1035 gboolean
1036 mono_thread_info_unified_management_enabled (void)
1037 {
1038         return unified_suspend_enabled;
1039 }
1040
1041 /*
1042  * mono_thread_info_set_is_async_context:
1043  *
1044  *   Set whenever the current thread is in an async context. Some runtime functions might behave
1045  * differently while in an async context in order to be async safe.
1046  */
1047 void
1048 mono_thread_info_set_is_async_context (gboolean async_context)
1049 {
1050         MonoThreadInfo *info = mono_thread_info_current ();
1051
1052         if (info)
1053                 info->is_async_context = async_context;
1054 }
1055
1056 gboolean
1057 mono_thread_info_is_async_context (void)
1058 {
1059         MonoThreadInfo *info = mono_thread_info_current ();
1060
1061         if (info)
1062                 return info->is_async_context;
1063         else
1064                 return FALSE;
1065 }
1066
1067 /*
1068  * mono_threads_create_thread:
1069  *
1070  *   Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1071  * Returns: a windows or io-layer handle for the thread.
1072  */
1073 HANDLE
1074 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1075 {
1076         return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1077 }
1078
1079 /*
1080  * mono_thread_info_get_stack_bounds:
1081  *
1082  *   Return the address and size of the current threads stack. Return NULL as the 
1083  * stack address if the stack address cannot be determined.
1084  */
1085 void
1086 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1087 {
1088         guint8 *current = (guint8 *)&stsize;
1089         mono_threads_core_get_stack_bounds (staddr, stsize);
1090         if (!*staddr)
1091                 return;
1092
1093         /* Sanity check the result */
1094         g_assert ((current > *staddr) && (current < *staddr + *stsize));
1095
1096         /* When running under emacs, sometimes staddr is not aligned to a page size */
1097         *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1098 }
1099
1100 gboolean
1101 mono_thread_info_yield (void)
1102 {
1103         return mono_threads_core_yield ();
1104 }
1105 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1106 static MonoCoopMutex sleep_mutex;
1107 static MonoCoopCond sleep_cond;
1108
1109 static void
1110 sleep_initialize (void)
1111 {
1112         mono_coop_mutex_init (&sleep_mutex);
1113         mono_coop_cond_init (&sleep_cond);
1114 }
1115
1116 static void
1117 sleep_interrupt (gpointer data)
1118 {
1119         mono_coop_mutex_lock (&sleep_mutex);
1120         mono_coop_cond_broadcast (&sleep_cond);
1121         mono_coop_mutex_unlock (&sleep_mutex);
1122 }
1123
1124 static inline guint32
1125 sleep_interruptable (guint32 ms, gboolean *alerted)
1126 {
1127         guint32 start, now, end;
1128
1129         g_assert (INFINITE == G_MAXUINT32);
1130
1131         g_assert (alerted);
1132         *alerted = FALSE;
1133
1134         start = mono_msec_ticks ();
1135
1136         if (start < G_MAXUINT32 - ms) {
1137                 end = start + ms;
1138         } else {
1139                 /* start + ms would overflow guint32 */
1140                 end = G_MAXUINT32;
1141         }
1142
1143         mono_lazy_initialize (&sleep_init, sleep_initialize);
1144
1145         mono_coop_mutex_lock (&sleep_mutex);
1146
1147         for (now = mono_msec_ticks (); ms == INFINITE || now - start < ms; now = mono_msec_ticks ()) {
1148                 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1149                 if (*alerted) {
1150                         mono_coop_mutex_unlock (&sleep_mutex);
1151                         return WAIT_IO_COMPLETION;
1152                 }
1153
1154                 if (ms < INFINITE)
1155                         mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1156                 else
1157                         mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1158
1159                 mono_thread_info_uninstall_interrupt (alerted);
1160                 if (*alerted) {
1161                         mono_coop_mutex_unlock (&sleep_mutex);
1162                         return WAIT_IO_COMPLETION;
1163                 }
1164         }
1165
1166         mono_coop_mutex_unlock (&sleep_mutex);
1167
1168         return 0;
1169 }
1170
1171 gint
1172 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1173 {
1174         if (ms == 0) {
1175                 MonoThreadInfo *info;
1176
1177                 mono_thread_info_yield ();
1178
1179                 info = mono_thread_info_current ();
1180                 if (info && mono_thread_info_is_interrupt_state (info))
1181                         return WAIT_IO_COMPLETION;
1182
1183                 return 0;
1184         }
1185
1186         if (alerted)
1187                 return sleep_interruptable (ms, alerted);
1188
1189         MONO_PREPARE_BLOCKING;
1190
1191         if (ms == INFINITE) {
1192                 do {
1193 #ifdef HOST_WIN32
1194                         Sleep (G_MAXUINT32);
1195 #else
1196                         sleep (G_MAXUINT32);
1197 #endif
1198                 } while (1);
1199         } else {
1200                 int ret;
1201 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1202                 struct timespec start, target;
1203
1204                 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1205                 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1206                 g_assert (ret == 0);
1207
1208                 target = start;
1209                 target.tv_sec += ms / 1000;
1210                 target.tv_nsec += (ms % 1000) * 1000000;
1211                 if (target.tv_nsec > 999999999) {
1212                         target.tv_nsec -= 999999999;
1213                         target.tv_sec ++;
1214                 }
1215
1216                 do {
1217                         ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1218                 } while (ret != 0);
1219 #elif HOST_WIN32
1220                 Sleep (ms);
1221 #else
1222                 struct timespec req, rem;
1223
1224                 req.tv_sec = ms / 1000;
1225                 req.tv_nsec = (ms % 1000) * 1000000;
1226
1227                 do {
1228                         memset (&rem, 0, sizeof (rem));
1229                         ret = nanosleep (&req, &rem);
1230                 } while (ret != 0);
1231 #endif /* __linux__ */
1232         }
1233
1234         MONO_FINISH_BLOCKING;
1235
1236         return 0;
1237 }
1238
1239 gint
1240 mono_thread_info_usleep (guint64 us)
1241 {
1242         MONO_PREPARE_BLOCKING;
1243         g_usleep (us);
1244         MONO_FINISH_BLOCKING;
1245         return 0;
1246 }
1247
1248 gpointer
1249 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1250 {
1251         return ((MonoThreadInfo*)info)->tls [key];
1252 }
1253
1254 /*
1255  * mono_threads_info_tls_set:
1256  *
1257  *   Set the TLS key to VALUE in the info structure. This can be used to obtain
1258  * values of TLS variables for threads other than the current thread.
1259  * This should only be used for infrequently changing TLS variables, and it should
1260  * be paired with setting the real TLS variable since this provides no GC tracking.
1261  */
1262 void
1263 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1264 {
1265         ((MonoThreadInfo*)info)->tls [key] = value;
1266 }
1267
1268 /*
1269  * mono_thread_info_exit:
1270  *
1271  *   Exit the current thread.
1272  * This function doesn't return.
1273  */
1274 void
1275 mono_thread_info_exit (void)
1276 {
1277         mono_threads_core_exit (0);
1278 }
1279
1280 /*
1281  * mono_thread_info_open_handle:
1282  *
1283  *   Return a io-layer/win32 handle for the current thread.
1284  * The handle need to be closed by calling CloseHandle () when it is no
1285  * longer needed.
1286  */
1287 HANDLE
1288 mono_thread_info_open_handle (void)
1289 {
1290         return mono_threads_core_open_handle ();
1291 }
1292
1293 /*
1294  * mono_threads_open_thread_handle:
1295  *
1296  *   Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1297  * The handle need to be closed by calling CloseHandle () when it is no
1298  * longer needed.
1299  */
1300 HANDLE
1301 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1302 {
1303         return mono_threads_core_open_thread_handle (handle, tid);
1304 }
1305
1306 void
1307 mono_thread_info_set_name (MonoNativeThreadId tid, const char *name)
1308 {
1309         mono_threads_core_set_name (tid, name);
1310 }
1311
1312 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1313
1314 struct _MonoThreadInfoInterruptToken {
1315         void (*callback) (gpointer data);
1316         gpointer data;
1317 };
1318
1319 /*
1320  * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1321  *
1322  *  - @callback: must be able to be called from another thread and always cancel the wait
1323  *  - @data: passed to the callback
1324  *  - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1325  *     if set to TRUE, it must mean that the thread is in interrupted state
1326  */
1327 void
1328 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1329 {
1330         MonoThreadInfo *info;
1331         MonoThreadInfoInterruptToken *previous_token, *token;
1332
1333         g_assert (callback);
1334
1335         g_assert (interrupted);
1336         *interrupted = FALSE;
1337
1338         info = mono_thread_info_current ();
1339         g_assert (info);
1340
1341         /* The memory of this token can be freed at 2 places:
1342          *  - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1343          *     by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1344          *  - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1345          *     functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1346         token = g_new0 (MonoThreadInfoInterruptToken, 1);
1347         token->callback = callback;
1348         token->data = data;
1349
1350         previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1351
1352         if (previous_token) {
1353                 if (previous_token != INTERRUPT_STATE)
1354                         g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1355
1356                 g_free (token);
1357
1358                 *interrupted = TRUE;
1359         }
1360
1361         THREADS_INTERRUPT_DEBUG ("interrupt install    tid %p token %p previous_token %p interrupted %s\n",
1362                 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1363 }
1364
1365 void
1366 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1367 {
1368         MonoThreadInfo *info;
1369         MonoThreadInfoInterruptToken *previous_token;
1370
1371         g_assert (interrupted);
1372         *interrupted = FALSE;
1373
1374         info = mono_thread_info_current ();
1375         g_assert (info);
1376
1377         previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1378
1379         /* only the installer can uninstall the token */
1380         g_assert (previous_token);
1381
1382         if (previous_token == INTERRUPT_STATE) {
1383                 /* if it is interrupted, then it is going to be freed in finish interrupt */
1384                 *interrupted = TRUE;
1385         } else {
1386                 g_free (previous_token);
1387         }
1388
1389         THREADS_INTERRUPT_DEBUG ("interrupt uninstall  tid %p previous_token %p interrupted %s\n",
1390                 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1391 }
1392
1393 static MonoThreadInfoInterruptToken*
1394 set_interrupt_state (MonoThreadInfo *info)
1395 {
1396         MonoThreadInfoInterruptToken *token, *previous_token;
1397
1398         g_assert (info);
1399
1400         /* Atomically obtain the token the thread is
1401         * waiting on, and change it to a flag value. */
1402
1403         do {
1404                 previous_token = info->interrupt_token;
1405
1406                 /* Already interrupted */
1407                 if (previous_token == INTERRUPT_STATE) {
1408                         token = NULL;
1409                         break;
1410                 }
1411
1412                 token = previous_token;
1413         } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1414
1415         return token;
1416 }
1417
1418 /*
1419  * mono_thread_info_prepare_interrupt:
1420  *
1421  * The state of the thread info interrupt token is set to 'interrupted' which means that :
1422  *  - if the thread calls one of the WaitFor functions, the function will return with
1423  *     WAIT_IO_COMPLETION instead of waiting
1424  *  - if the thread was waiting when this function was called, the wait will be broken
1425  *
1426  * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1427  * didn't receive the interrupt signal yet, in this case it should call the wait function
1428  * again. This essentially means that the target thread will busy wait until it is ready to
1429  * process the interruption.
1430  */
1431 MonoThreadInfoInterruptToken*
1432 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1433 {
1434         MonoThreadInfoInterruptToken *token;
1435
1436         token = set_interrupt_state (info);
1437
1438         THREADS_INTERRUPT_DEBUG ("interrupt prepare    tid %p token %p\n",
1439                 mono_thread_info_get_tid (info), token);
1440
1441         return token;
1442 }
1443
1444 void
1445 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1446 {
1447         THREADS_INTERRUPT_DEBUG ("interrupt finish     token %p\n", token);
1448
1449         if (token == NULL)
1450                 return;
1451
1452         g_assert (token->callback);
1453
1454         token->callback (token->data);
1455
1456         g_free (token);
1457 }
1458
1459 void
1460 mono_thread_info_self_interrupt (void)
1461 {
1462         MonoThreadInfo *info;
1463         MonoThreadInfoInterruptToken *token;
1464
1465         info = mono_thread_info_current ();
1466         g_assert (info);
1467
1468         token = set_interrupt_state (info);
1469         g_assert (!token);
1470
1471         THREADS_INTERRUPT_DEBUG ("interrupt self       tid %p\n",
1472                 mono_thread_info_get_tid (info));
1473 }
1474
1475 /* Clear the interrupted flag of the current thread, set with
1476  * mono_thread_info_self_interrupt, so it can wait again */
1477 void
1478 mono_thread_info_clear_self_interrupt ()
1479 {
1480         MonoThreadInfo *info;
1481         MonoThreadInfoInterruptToken *previous_token;
1482
1483         info = mono_thread_info_current ();
1484         g_assert (info);
1485
1486         previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1487         g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1488
1489         THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1490 }
1491
1492 gboolean
1493 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1494 {
1495         g_assert (info);
1496         return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1497 }
1498
1499 void
1500 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1501 {
1502         g_assert (info);
1503
1504         if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1505                 g_string_append_printf (text, "not waiting");
1506         else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1507                 g_string_append_printf (text, "interrupted state");
1508         else
1509                 g_string_append_printf (text, "waiting");
1510 }
1511
1512 /* info must be self or be held in a hazard pointer. */
1513 gboolean
1514 mono_threads_add_async_job (MonoThreadInfo *info, MonoAsyncJob job)
1515 {
1516         MonoAsyncJob old_job;
1517         do {
1518                 old_job = (MonoAsyncJob) info->service_requests;
1519                 if (old_job & job)
1520                         return FALSE;
1521         } while (InterlockedCompareExchange (&info->service_requests, old_job | job, old_job) != old_job);
1522         return TRUE;
1523 }
1524
1525 MonoAsyncJob
1526 mono_threads_consume_async_jobs (void)
1527 {
1528         MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
1529
1530         if (!info)
1531                 return (MonoAsyncJob) 0;
1532
1533         return (MonoAsyncJob) InterlockedExchange (&info->service_requests, 0);
1534 }