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