Halve the stack depth for both the new single step tests
[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 /*
800 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
801 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
802 */
803 static gboolean
804 is_thread_in_critical_region (MonoThreadInfo *info)
805 {
806         MonoMethod *method;
807         MonoJitInfo *ji;
808         gpointer stack_start;
809         MonoThreadUnwindState *state;
810
811         /* Are we inside a system critical region? */
812         if (info->inside_critical_region)
813                 return TRUE;
814
815         /* Are we inside a GC critical region? */
816         if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
817                 return TRUE;
818         }
819
820         /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
821         state = mono_thread_info_get_suspend_state (info);
822         if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
823                 return FALSE;
824
825         stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
826         /* altstack signal handler, sgen can't handle them, so we treat them as critical */
827         if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
828                 return TRUE;
829
830         ji = mono_jit_info_table_find (
831                 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
832                 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
833
834         if (!ji)
835                 return FALSE;
836
837         method = mono_jit_info_get_method (ji);
838
839         return threads_callbacks.mono_method_is_critical (method);
840 }
841
842 gboolean
843 mono_thread_info_in_critical_location (MonoThreadInfo *info)
844 {
845         return is_thread_in_critical_region (info);
846 }
847
848 /*
849 The return value is only valid until a matching mono_thread_info_resume is called
850 */
851 static MonoThreadInfo*
852 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
853 {
854         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
855         MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
856         if (!info)
857                 return NULL;
858
859         switch (mono_threads_transition_request_async_suspension (info)) {
860         case AsyncSuspendAlreadySuspended:
861                 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
862                 return info;
863         case AsyncSuspendWait:
864                 mono_threads_add_to_pending_operation_set (info);
865                 break;
866         case AsyncSuspendInitSuspend:
867                 if (!begin_async_suspend (info, interrupt_kernel)) {
868                         mono_hazard_pointer_clear (hp, 1);
869                         return NULL;
870                 }
871                 break;
872         case AsyncSuspendBlocking:
873                 if (interrupt_kernel)
874                         mono_threads_core_abort_syscall (info);
875
876                 break;
877         default:
878                 g_assert_not_reached ();
879         }
880
881         //Wait for the pending suspend to finish
882         mono_threads_wait_pending_operations ();
883
884         if (!check_async_suspend (info)) {
885                 mono_thread_info_core_resume (info);
886                 mono_threads_wait_pending_operations ();
887                 mono_hazard_pointer_clear (hp, 1);
888                 return NULL;
889         }
890         return info;
891 }
892
893 static MonoThreadInfo*
894 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
895 {
896         MonoThreadInfo *info = NULL;
897         int sleep_duration = 0;
898         for (;;) {
899                 if (!(info = suspend_sync (id, interrupt_kernel))) {
900                         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
901                         return NULL;
902                 }
903
904                 /*WARNING: We now are in interrupt context until we resume the thread. */
905                 if (!is_thread_in_critical_region (info))
906                         break;
907
908                 if (!mono_thread_info_core_resume (info)) {
909                         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
910                         return NULL;
911                 }
912                 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
913
914                 /* Wait for the pending resume to finish */
915                 mono_threads_wait_pending_operations ();
916
917                 if (sleep_duration == 0)
918                         mono_thread_info_yield ();
919                 else
920                         g_usleep (sleep_duration);
921
922                 sleep_duration += 10;
923         }
924         return info;
925 }
926
927 void
928 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
929 {
930         int result;
931         MonoThreadInfo *info = NULL;
932         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
933
934         THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
935         /*FIXME: unify this with self-suspend*/
936         g_assert (id != mono_native_thread_id_get ());
937
938         /* This can block during stw */
939         mono_thread_info_suspend_lock ();
940         mono_threads_begin_global_suspend ();
941
942         info = suspend_sync_nolock (id, interrupt_kernel);
943         if (!info)
944                 goto done;
945
946         switch (result = callback (info, user_data)) {
947         case MonoResumeThread:
948                 mono_hazard_pointer_set (hp, 1, info);
949                 mono_thread_info_core_resume (info);
950                 mono_threads_wait_pending_operations ();
951                 break;
952         case KeepSuspended:
953                 break;
954         default:
955                 g_error ("Invalid suspend_and_run callback return value %d", result);
956         }
957
958 done:
959         mono_hazard_pointer_clear (hp, 1);
960         mono_threads_end_global_suspend ();
961         mono_thread_info_suspend_unlock ();
962 }
963
964 /**
965 Inject an assynchronous call into the target thread. The target thread must be suspended and
966 only a single async call can be setup for a given suspend cycle.
967 This async call must cause stack unwinding as the current implementation doesn't save enough state
968 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
969 currently used only to deliver exceptions.
970 */
971 void
972 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
973 {
974         /* An async call can only be setup on an async suspended thread */
975         g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
976         /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
977         g_assert (!info->async_target);
978         info->async_target = target_func;
979         /* This is not GC tracked */
980         info->user_data = user_data;
981 }
982
983 /*
984 The suspend lock is held during any suspend in progress.
985 A GC that has safepoints must take this lock as part of its
986 STW to make sure no unsafe pending suspend is in progress.   
987 */
988 void
989 mono_thread_info_suspend_lock (void)
990 {
991         int res = mono_coop_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
992         g_assert (res != -1);
993 }
994
995 void
996 mono_thread_info_suspend_unlock (void)
997 {
998         mono_coop_sem_post (&global_suspend_semaphore);
999 }
1000
1001 /*
1002  * This is a very specific function whose only purpose is to
1003  * break a given thread from socket syscalls.
1004  *
1005  * This only exists because linux won't fail a call to connect
1006  * if the underlying is closed.
1007  *
1008  * TODO We should cleanup and unify this with the other syscall abort
1009  * facility.
1010  */
1011 void
1012 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1013 {
1014         MonoThreadHazardPointers *hp;
1015         MonoThreadInfo *info;
1016
1017         if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ())
1018                 return;
1019
1020         hp = mono_hazard_pointer_get ();
1021         info = mono_thread_info_lookup (tid);
1022         if (!info)
1023                 return;
1024
1025         if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1026                 mono_hazard_pointer_clear (hp, 1);
1027                 return;
1028         }
1029
1030         mono_thread_info_suspend_lock ();
1031         mono_threads_begin_global_suspend ();
1032
1033         mono_threads_core_abort_syscall (info);
1034         mono_threads_wait_pending_operations ();
1035
1036         mono_hazard_pointer_clear (hp, 1);
1037
1038         mono_threads_end_global_suspend ();
1039         mono_thread_info_suspend_unlock ();
1040 }
1041
1042 gboolean
1043 mono_thread_info_unified_management_enabled (void)
1044 {
1045         return unified_suspend_enabled;
1046 }
1047
1048 /*
1049  * mono_thread_info_set_is_async_context:
1050  *
1051  *   Set whenever the current thread is in an async context. Some runtime functions might behave
1052  * differently while in an async context in order to be async safe.
1053  */
1054 void
1055 mono_thread_info_set_is_async_context (gboolean async_context)
1056 {
1057         MonoThreadInfo *info = mono_thread_info_current ();
1058
1059         if (info)
1060                 info->is_async_context = async_context;
1061 }
1062
1063 gboolean
1064 mono_thread_info_is_async_context (void)
1065 {
1066         MonoThreadInfo *info = mono_thread_info_current ();
1067
1068         if (info)
1069                 return info->is_async_context;
1070         else
1071                 return FALSE;
1072 }
1073
1074 /*
1075  * mono_threads_create_thread:
1076  *
1077  *   Create a new thread executing START with argument ARG. Store its id into OUT_TID.
1078  * Returns: a windows or io-layer handle for the thread.
1079  */
1080 HANDLE
1081 mono_threads_create_thread (LPTHREAD_START_ROUTINE start, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
1082 {
1083         return mono_threads_core_create_thread (start, arg, stack_size, creation_flags, out_tid);
1084 }
1085
1086 /*
1087  * mono_thread_info_get_stack_bounds:
1088  *
1089  *   Return the address and size of the current threads stack. Return NULL as the 
1090  * stack address if the stack address cannot be determined.
1091  */
1092 void
1093 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1094 {
1095         guint8 *current = (guint8 *)&stsize;
1096         mono_threads_core_get_stack_bounds (staddr, stsize);
1097         if (!*staddr)
1098                 return;
1099
1100         /* Sanity check the result */
1101         g_assert ((current > *staddr) && (current < *staddr + *stsize));
1102
1103         /* When running under emacs, sometimes staddr is not aligned to a page size */
1104         *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1105 }
1106
1107 gboolean
1108 mono_thread_info_yield (void)
1109 {
1110         return mono_threads_core_yield ();
1111 }
1112 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1113 static MonoCoopMutex sleep_mutex;
1114 static MonoCoopCond sleep_cond;
1115
1116 static void
1117 sleep_initialize (void)
1118 {
1119         mono_coop_mutex_init (&sleep_mutex);
1120         mono_coop_cond_init (&sleep_cond);
1121 }
1122
1123 static void
1124 sleep_interrupt (gpointer data)
1125 {
1126         mono_coop_mutex_lock (&sleep_mutex);
1127         mono_coop_cond_broadcast (&sleep_cond);
1128         mono_coop_mutex_unlock (&sleep_mutex);
1129 }
1130
1131 static inline guint32
1132 sleep_interruptable (guint32 ms, gboolean *alerted)
1133 {
1134         gint64 now, end;
1135
1136         g_assert (INFINITE == G_MAXUINT32);
1137
1138         g_assert (alerted);
1139         *alerted = FALSE;
1140
1141         if (ms != INFINITE)
1142                 end = mono_100ns_ticks () + (ms * 1000 * 10);
1143
1144         mono_lazy_initialize (&sleep_init, sleep_initialize);
1145
1146         mono_coop_mutex_lock (&sleep_mutex);
1147
1148         for (;;) {
1149                 if (ms != INFINITE) {
1150                         now = mono_100ns_ticks ();
1151                         if (now > end)
1152                                 break;
1153                 }
1154
1155                 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1156                 if (*alerted) {
1157                         mono_coop_mutex_unlock (&sleep_mutex);
1158                         return WAIT_IO_COMPLETION;
1159                 }
1160
1161                 if (ms != INFINITE)
1162                         mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, (end - now) / 10 / 1000);
1163                 else
1164                         mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1165
1166                 mono_thread_info_uninstall_interrupt (alerted);
1167                 if (*alerted) {
1168                         mono_coop_mutex_unlock (&sleep_mutex);
1169                         return WAIT_IO_COMPLETION;
1170                 }
1171         }
1172
1173         mono_coop_mutex_unlock (&sleep_mutex);
1174
1175         return 0;
1176 }
1177
1178 gint
1179 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1180 {
1181         if (ms == 0) {
1182                 MonoThreadInfo *info;
1183
1184                 mono_thread_info_yield ();
1185
1186                 info = mono_thread_info_current ();
1187                 if (info && mono_thread_info_is_interrupt_state (info))
1188                         return WAIT_IO_COMPLETION;
1189
1190                 return 0;
1191         }
1192
1193         if (alerted)
1194                 return sleep_interruptable (ms, alerted);
1195
1196         MONO_ENTER_GC_SAFE;
1197
1198         if (ms == INFINITE) {
1199                 do {
1200 #ifdef HOST_WIN32
1201                         Sleep (G_MAXUINT32);
1202 #else
1203                         sleep (G_MAXUINT32);
1204 #endif
1205                 } while (1);
1206         } else {
1207                 int ret;
1208 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1209                 struct timespec start, target;
1210
1211                 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1212                 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1213                 g_assert (ret == 0);
1214
1215                 target = start;
1216                 target.tv_sec += ms / 1000;
1217                 target.tv_nsec += (ms % 1000) * 1000000;
1218                 if (target.tv_nsec > 999999999) {
1219                         target.tv_nsec -= 999999999;
1220                         target.tv_sec ++;
1221                 }
1222
1223                 do {
1224                         ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1225                 } while (ret != 0);
1226 #elif HOST_WIN32
1227                 Sleep (ms);
1228 #else
1229                 struct timespec req, rem;
1230
1231                 req.tv_sec = ms / 1000;
1232                 req.tv_nsec = (ms % 1000) * 1000000;
1233
1234                 do {
1235                         memset (&rem, 0, sizeof (rem));
1236                         ret = nanosleep (&req, &rem);
1237                 } while (ret != 0);
1238 #endif /* __linux__ */
1239         }
1240
1241         MONO_EXIT_GC_SAFE;
1242
1243         return 0;
1244 }
1245
1246 gint
1247 mono_thread_info_usleep (guint64 us)
1248 {
1249         MONO_ENTER_GC_SAFE;
1250         g_usleep (us);
1251         MONO_EXIT_GC_SAFE;
1252         return 0;
1253 }
1254
1255 gpointer
1256 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1257 {
1258         return ((MonoThreadInfo*)info)->tls [key];
1259 }
1260
1261 /*
1262  * mono_threads_info_tls_set:
1263  *
1264  *   Set the TLS key to VALUE in the info structure. This can be used to obtain
1265  * values of TLS variables for threads other than the current thread.
1266  * This should only be used for infrequently changing TLS variables, and it should
1267  * be paired with setting the real TLS variable since this provides no GC tracking.
1268  */
1269 void
1270 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1271 {
1272         ((MonoThreadInfo*)info)->tls [key] = value;
1273 }
1274
1275 /*
1276  * mono_thread_info_exit:
1277  *
1278  *   Exit the current thread.
1279  * This function doesn't return.
1280  */
1281 void
1282 mono_thread_info_exit (void)
1283 {
1284         mono_threads_core_exit (0);
1285 }
1286
1287 /*
1288  * mono_thread_info_open_handle:
1289  *
1290  *   Return a io-layer/win32 handle for the current thread.
1291  * The handle need to be closed by calling CloseHandle () when it is no
1292  * longer needed.
1293  */
1294 HANDLE
1295 mono_thread_info_open_handle (void)
1296 {
1297         return mono_threads_core_open_handle ();
1298 }
1299
1300 /*
1301  * mono_threads_open_thread_handle:
1302  *
1303  *   Return a io-layer/win32 handle for the thread identified by HANDLE/TID.
1304  * The handle need to be closed by calling CloseHandle () when it is no
1305  * longer needed.
1306  */
1307 HANDLE
1308 mono_threads_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
1309 {
1310         return mono_threads_core_open_thread_handle (handle, tid);
1311 }
1312
1313 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1314
1315 struct _MonoThreadInfoInterruptToken {
1316         void (*callback) (gpointer data);
1317         gpointer data;
1318 };
1319
1320 /*
1321  * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1322  *
1323  *  - @callback: must be able to be called from another thread and always cancel the wait
1324  *  - @data: passed to the callback
1325  *  - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1326  *     if set to TRUE, it must mean that the thread is in interrupted state
1327  */
1328 void
1329 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1330 {
1331         MonoThreadInfo *info;
1332         MonoThreadInfoInterruptToken *previous_token, *token;
1333
1334         g_assert (callback);
1335
1336         g_assert (interrupted);
1337         *interrupted = FALSE;
1338
1339         info = mono_thread_info_current ();
1340         g_assert (info);
1341
1342         /* The memory of this token can be freed at 2 places:
1343          *  - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1344          *     by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1345          *  - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1346          *     functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1347         token = g_new0 (MonoThreadInfoInterruptToken, 1);
1348         token->callback = callback;
1349         token->data = data;
1350
1351         previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1352
1353         if (previous_token) {
1354                 if (previous_token != INTERRUPT_STATE)
1355                         g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1356
1357                 g_free (token);
1358
1359                 *interrupted = TRUE;
1360         }
1361
1362         THREADS_INTERRUPT_DEBUG ("interrupt install    tid %p token %p previous_token %p interrupted %s\n",
1363                 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1364 }
1365
1366 void
1367 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1368 {
1369         MonoThreadInfo *info;
1370         MonoThreadInfoInterruptToken *previous_token;
1371
1372         g_assert (interrupted);
1373         *interrupted = FALSE;
1374
1375         info = mono_thread_info_current ();
1376         g_assert (info);
1377
1378         previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1379
1380         /* only the installer can uninstall the token */
1381         g_assert (previous_token);
1382
1383         if (previous_token == INTERRUPT_STATE) {
1384                 /* if it is interrupted, then it is going to be freed in finish interrupt */
1385                 *interrupted = TRUE;
1386         } else {
1387                 g_free (previous_token);
1388         }
1389
1390         THREADS_INTERRUPT_DEBUG ("interrupt uninstall  tid %p previous_token %p interrupted %s\n",
1391                 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1392 }
1393
1394 static MonoThreadInfoInterruptToken*
1395 set_interrupt_state (MonoThreadInfo *info)
1396 {
1397         MonoThreadInfoInterruptToken *token, *previous_token;
1398
1399         g_assert (info);
1400
1401         /* Atomically obtain the token the thread is
1402         * waiting on, and change it to a flag value. */
1403
1404         do {
1405                 previous_token = info->interrupt_token;
1406
1407                 /* Already interrupted */
1408                 if (previous_token == INTERRUPT_STATE) {
1409                         token = NULL;
1410                         break;
1411                 }
1412
1413                 token = previous_token;
1414         } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1415
1416         return token;
1417 }
1418
1419 /*
1420  * mono_thread_info_prepare_interrupt:
1421  *
1422  * The state of the thread info interrupt token is set to 'interrupted' which means that :
1423  *  - if the thread calls one of the WaitFor functions, the function will return with
1424  *     WAIT_IO_COMPLETION instead of waiting
1425  *  - if the thread was waiting when this function was called, the wait will be broken
1426  *
1427  * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1428  * didn't receive the interrupt signal yet, in this case it should call the wait function
1429  * again. This essentially means that the target thread will busy wait until it is ready to
1430  * process the interruption.
1431  */
1432 MonoThreadInfoInterruptToken*
1433 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1434 {
1435         MonoThreadInfoInterruptToken *token;
1436
1437         token = set_interrupt_state (info);
1438
1439         THREADS_INTERRUPT_DEBUG ("interrupt prepare    tid %p token %p\n",
1440                 mono_thread_info_get_tid (info), token);
1441
1442         return token;
1443 }
1444
1445 void
1446 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1447 {
1448         THREADS_INTERRUPT_DEBUG ("interrupt finish     token %p\n", token);
1449
1450         if (token == NULL)
1451                 return;
1452
1453         g_assert (token->callback);
1454
1455         token->callback (token->data);
1456
1457         g_free (token);
1458 }
1459
1460 void
1461 mono_thread_info_self_interrupt (void)
1462 {
1463         MonoThreadInfo *info;
1464         MonoThreadInfoInterruptToken *token;
1465
1466         info = mono_thread_info_current ();
1467         g_assert (info);
1468
1469         token = set_interrupt_state (info);
1470         g_assert (!token);
1471
1472         THREADS_INTERRUPT_DEBUG ("interrupt self       tid %p\n",
1473                 mono_thread_info_get_tid (info));
1474 }
1475
1476 /* Clear the interrupted flag of the current thread, set with
1477  * mono_thread_info_self_interrupt, so it can wait again */
1478 void
1479 mono_thread_info_clear_self_interrupt ()
1480 {
1481         MonoThreadInfo *info;
1482         MonoThreadInfoInterruptToken *previous_token;
1483
1484         info = mono_thread_info_current ();
1485         g_assert (info);
1486
1487         previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1488         g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1489
1490         THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1491 }
1492
1493 gboolean
1494 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1495 {
1496         g_assert (info);
1497         return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1498 }
1499
1500 void
1501 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1502 {
1503         g_assert (info);
1504
1505         if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1506                 g_string_append_printf (text, "not waiting");
1507         else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1508                 g_string_append_printf (text, "interrupted state");
1509         else
1510                 g_string_append_printf (text, "waiting");
1511 }