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