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