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