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