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