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