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