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