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