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