[interp] Remove unconditional branches to next instruction (#15939)
[mono-project.git] / mono / utils / mono-threads-coop.c
blob3e22e7fa8c235a09cf484450446be1820a53c447
1 /**
2 * \file
3 * Coop threading
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <config.h>
14 /* enable pthread extensions */
15 #ifdef TARGET_MACH
16 #define _DARWIN_C_SOURCE
17 #endif
19 #include <mono/utils/mono-compiler.h>
20 #include <mono/utils/mono-threads.h>
21 #include <mono/utils/mono-tls.h>
22 #include <mono/utils/hazard-pointer.h>
23 #include <mono/utils/mono-memory-model.h>
24 #include <mono/utils/mono-mmap.h>
25 #include <mono/utils/atomic.h>
26 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mono-counters.h>
28 #include <mono/utils/mono-threads-coop.h>
29 #include <mono/utils/mono-threads-api.h>
30 #include <mono/utils/checked-build.h>
31 #include <mono/utils/mono-threads-debug.h>
33 #ifdef TARGET_OSX
34 #include <mono/utils/mach-support.h>
35 #endif
37 /* On platforms that doesn't have full context support (or doesn't do conservative stack scan), use copy stack data */
38 /* when entering safe/unsafe GC regions. For platforms with full context support (doing conservative stack scan), */
39 /* there is already logic in place to take context before getting in a state where thread could be conservative */
40 /* scanned by GC. Avoiding doing additional stack copy will increse performance when entering safe/unsafe regions */
41 /* when running in hybrid/cooperative supspend mode. */
42 #if defined (ENABLE_COPY_STACK_DATA)
43 #ifdef _MSC_VER
44 // __builtin_unwind_init not available under MSVC but equivalent implementation is done using
45 // copy_stack_data_internal_win32_wrapper.
46 #define SAVE_REGS_ON_STACK do {} while (0)
47 #elif defined (HOST_WASM)
48 //TODO: figure out wasm stack scanning
49 #define SAVE_REGS_ON_STACK do {} while (0)
50 #else
51 #define SAVE_REGS_ON_STACK __builtin_unwind_init ();
52 #endif
53 #else
54 #define SAVE_REGS_ON_STACK do {} while (0)
55 #endif
57 volatile size_t mono_polling_required;
59 // FIXME: This would be more efficient if instead of instantiating the stack it just pushed a simple depth counter up and down,
60 // perhaps with a per-thread cookie in the high bits.
61 #ifdef ENABLE_CHECKED_BUILD_GC
63 // Maintains a single per-thread stack of ints, used to ensure nesting is not violated
64 static MonoNativeTlsKey coop_reset_count_stack_key;
66 static void
67 coop_tls_push (gpointer cookie)
69 GArray *stack;
71 stack = (GArray*)mono_native_tls_get_value (coop_reset_count_stack_key);
72 if (!stack) {
73 stack = g_array_new (FALSE, FALSE, sizeof(gpointer));
74 mono_native_tls_set_value (coop_reset_count_stack_key, stack);
77 g_array_append_val (stack, cookie);
80 static void
81 coop_tls_pop (gpointer received_cookie)
83 GArray *stack;
84 gpointer expected_cookie;
86 stack = (GArray*)mono_native_tls_get_value (coop_reset_count_stack_key);
87 if (!stack || 0 == stack->len)
88 mono_fatal_with_history ("Received cookie %p but found no stack at all\n", received_cookie);
90 expected_cookie = g_array_index (stack, gpointer, stack->len - 1);
91 stack->len --;
93 if (0 == stack->len) {
94 g_array_free (stack,TRUE);
95 mono_native_tls_set_value (coop_reset_count_stack_key, NULL);
98 if (expected_cookie != received_cookie)
99 mono_fatal_with_history ("Received cookie %p but expected %p\n", received_cookie, expected_cookie);
102 #endif
104 static void
105 check_info (MonoThreadInfo *info, const gchar *action, const gchar *state, const char *func)
107 if (!info)
108 g_error ("%s Cannot %s GC %s region if the thread is not attached", func, action, state);
109 if (!mono_thread_info_is_current (info))
110 g_error ("%s [%p] Cannot %s GC %s region on a different thread", func, mono_thread_info_get_tid (info), action, state);
111 if (!mono_thread_info_is_live (info))
112 g_error ("%s [%p] Cannot %s GC %s region if the thread is not live", func, mono_thread_info_get_tid (info), action, state);
115 static int coop_reset_blocking_count;
116 static int coop_try_blocking_count;
117 static int coop_do_blocking_count;
118 static int coop_do_polling_count;
119 static int coop_save_count;
121 static void
122 mono_threads_state_poll_with_info (MonoThreadInfo *info);
124 void
125 mono_threads_state_poll (void)
127 mono_threads_state_poll_with_info (mono_thread_info_current_unchecked ());
130 static void
131 mono_threads_state_poll_with_info (MonoThreadInfo *info)
133 g_assert (mono_threads_is_blocking_transition_enabled ());
135 ++coop_do_polling_count;
137 if (!info)
138 return;
140 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
142 /* Fast fail if no_safepoints is set */
143 g_assert (!(info->thread_state & THREAD_SUSPEND_NO_SAFEPOINTS_MASK));
145 /* Fast check for pending suspend requests */
146 if (!(info->thread_state & STATE_ASYNC_SUSPEND_REQUESTED))
147 return;
149 ++coop_save_count;
150 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
152 /* commit the saved state and notify others if needed */
153 switch (mono_threads_transition_state_poll (info)) {
154 case SelfSuspendResumed:
155 break;
156 case SelfSuspendNotifyAndWait:
157 mono_threads_notify_initiator_of_suspend (info);
158 mono_thread_info_wait_for_resume (info);
159 break;
162 if (info->async_target) {
163 info->async_target (info->user_data);
164 info->async_target = NULL;
165 info->user_data = NULL;
169 static volatile gpointer* dummy_global;
171 static MONO_NEVER_INLINE
172 void*
173 return_stack_ptr (gpointer *i)
175 dummy_global = i;
176 return i;
179 static void
180 copy_stack_data_internal (MonoThreadInfo *info, MonoStackData *stackdata_begin, gconstpointer wrapper_data1, gconstpointer wrapper_data2)
182 MonoThreadUnwindState *state;
183 int stackdata_size;
184 gpointer dummy;
185 void* stackdata_end = return_stack_ptr (&dummy);
187 SAVE_REGS_ON_STACK;
189 state = &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
191 stackdata_size = (char*)mono_stackdata_get_stackpointer (stackdata_begin) - (char*)stackdata_end;
193 const char *function_name = mono_stackdata_get_function_name (stackdata_begin);
195 if (((gsize) stackdata_begin & (SIZEOF_VOID_P - 1)) != 0)
196 g_error ("%s stackdata_begin (%p) must be %d-byte aligned", function_name, stackdata_begin, SIZEOF_VOID_P);
197 if (((gsize) stackdata_end & (SIZEOF_VOID_P - 1)) != 0)
198 g_error ("%s stackdata_end (%p) must be %d-byte aligned", function_name, stackdata_end, SIZEOF_VOID_P);
200 if (stackdata_size <= 0)
201 g_error ("%s stackdata_size = %d, but must be > 0, stackdata_begin = %p, stackdata_end = %p", function_name, stackdata_size, stackdata_begin, stackdata_end);
203 g_byte_array_set_size (info->stackdata, stackdata_size);
204 state->gc_stackdata = info->stackdata->data;
205 memcpy (state->gc_stackdata, stackdata_end, stackdata_size);
207 state->gc_stackdata_size = stackdata_size;
210 #if defined (ENABLE_COPY_STACK_DATA)
211 #ifdef _MSC_VER
212 typedef void (*CopyStackDataFunc)(MonoThreadInfo *, MonoStackData *, gconstpointer, gconstpointer);
214 #ifdef TARGET_AMD64
215 // Implementation of __builtin_unwind_init under MSVC, dumping nonvolatile registers into MonoBuiltinUnwindInfo.
216 typedef struct {
217 __m128d fregs [10];
218 host_mgreg_t gregs [8];
219 } MonoBuiltinUnwindInfo;
221 // Defined in win64.asm
222 G_EXTERN_C void
223 copy_stack_data_internal_win32_wrapper (MonoThreadInfo *, MonoStackData *, MonoBuiltinUnwindInfo *, CopyStackDataFunc);
224 #else
225 // Implementation of __builtin_unwind_init under MSVC, dumping nonvolatile registers into MonoBuiltinUnwindInfo.
226 typedef struct {
227 host_mgreg_t gregs [4];
228 } MonoBuiltinUnwindInfo;
230 // Implementation of __builtin_unwind_init under MSVC, dumping nonvolatile registers into MonoBuiltinUnwindInfo *.
231 __declspec(naked) void __cdecl
232 copy_stack_data_internal_win32_wrapper (MonoThreadInfo *info, MonoStackData *stackdata_begin, MonoBuiltinUnwindInfo *unwind_info_data, CopyStackDataFunc func)
234 __asm {
235 mov edx, dword ptr [esp + 0Ch]
236 mov dword ptr [edx + 00h], ebx
237 mov dword ptr [edx + 04h], esi
238 mov dword ptr [edx + 08h], edi
239 mov dword ptr [edx + 0Ch], ebp
241 // tailcall, all parameters passed through to CopyStackDataFunc.
242 mov edx, dword ptr [esp + 10h]
243 jmp edx
246 #endif
248 static void
249 copy_stack_data (MonoThreadInfo *info, MonoStackData *stackdata_begin)
251 MonoBuiltinUnwindInfo unwind_info_data;
252 copy_stack_data_internal_win32_wrapper (info, stackdata_begin, &unwind_info_data, copy_stack_data_internal);
254 #else
255 static void
256 copy_stack_data (MonoThreadInfo *info, MonoStackData *stackdata_begin)
258 copy_stack_data_internal (info, stackdata_begin, NULL, NULL);
260 #endif
261 #else
262 static void
263 copy_stack_data (MonoThreadInfo *info, MonoStackData *stackdata_begin)
266 #endif
268 static gpointer
269 mono_threads_enter_gc_safe_region_unbalanced_with_info (MonoThreadInfo *info, MonoStackData *stackdata);
271 gpointer
272 mono_threads_enter_gc_safe_region_internal (MonoStackData *stackdata)
274 return mono_threads_enter_gc_safe_region_with_info (mono_thread_info_current_unchecked (), stackdata);
277 gpointer
278 mono_threads_enter_gc_safe_region (gpointer *stackpointer)
280 MONO_STACKDATA (stackdata);
281 stackdata.stackpointer = stackpointer;
282 return mono_threads_enter_gc_safe_region_internal (&stackdata);
285 gpointer
286 mono_threads_enter_gc_safe_region_with_info (MonoThreadInfo *info, MonoStackData *stackdata)
288 gpointer cookie;
290 if (!mono_threads_is_blocking_transition_enabled ())
291 return NULL;
293 cookie = mono_threads_enter_gc_safe_region_unbalanced_with_info (info, stackdata);
295 #ifdef ENABLE_CHECKED_BUILD_GC
296 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
297 coop_tls_push (cookie);
298 #endif
300 return cookie;
303 gpointer
304 mono_threads_enter_gc_safe_region_unbalanced_internal (MonoStackData *stackdata)
306 return mono_threads_enter_gc_safe_region_unbalanced_with_info (mono_thread_info_current_unchecked (), stackdata);
309 gpointer
310 mono_threads_enter_gc_safe_region_unbalanced (gpointer *stackpointer)
312 MONO_STACKDATA (stackdata);
313 stackdata.stackpointer = stackpointer;
314 return mono_threads_enter_gc_safe_region_unbalanced_internal (&stackdata);
317 static gpointer
318 mono_threads_enter_gc_safe_region_unbalanced_with_info (MonoThreadInfo *info, MonoStackData *stackdata)
320 if (!mono_threads_is_blocking_transition_enabled ())
321 return NULL;
323 ++coop_do_blocking_count;
325 const char *function_name = mono_stackdata_get_function_name (stackdata);
327 check_info (info, "enter", "safe", function_name);
329 copy_stack_data (info, stackdata);
331 retry:
332 ++coop_save_count;
333 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
335 switch (mono_threads_transition_do_blocking (info, function_name)) {
336 case DoBlockingContinue:
337 break;
338 case DoBlockingPollAndRetry:
339 mono_threads_state_poll_with_info (info);
340 goto retry;
343 return info;
346 void
347 mono_threads_exit_gc_safe_region_internal (gpointer cookie, MonoStackData *stackdata)
349 if (!mono_threads_is_blocking_transition_enabled ())
350 return;
352 #ifdef ENABLE_CHECKED_BUILD_GC
353 W32_DEFINE_LAST_ERROR_RESTORE_POINT;
354 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
355 coop_tls_pop (cookie);
356 W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
357 #endif
359 mono_threads_exit_gc_safe_region_unbalanced_internal (cookie, stackdata);
362 void
363 mono_threads_exit_gc_safe_region (gpointer cookie, gpointer *stackpointer)
365 MONO_STACKDATA (stackdata);
366 stackdata.stackpointer = stackpointer;
367 mono_threads_exit_gc_safe_region_internal (cookie, &stackdata);
370 void
371 mono_threads_exit_gc_safe_region_unbalanced_internal (gpointer cookie, MonoStackData *stackdata)
373 MonoThreadInfo *info;
375 if (!mono_threads_is_blocking_transition_enabled ())
376 return;
378 /* Common to use enter/exit gc safe around OS API's affecting last error. */
379 /* This method can call OS API's that will reset last error on some platforms. */
380 /* To reduce errors, we need to restore last error before exit gc safe. */
381 W32_DEFINE_LAST_ERROR_RESTORE_POINT;
383 info = (MonoThreadInfo *)cookie;
385 const char *function_name = mono_stackdata_get_function_name (stackdata);
387 check_info (info, "exit", "safe", function_name);
389 switch (mono_threads_transition_done_blocking (info, function_name)) {
390 case DoneBlockingOk:
391 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
392 break;
393 case DoneBlockingWait:
394 /* If full coop suspend, we're just waiting for the initiator
395 * to resume us. If hybrid suspend, we were either self
396 * suspended cooperatively from async_suspend_requested (same
397 * as full coop), or we were suspended preemptively while in
398 * blocking and we're waiting for two things: the suspend
399 * signal handler to run and notify the initiator and
400 * immediately return, and then for the resume. */
401 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
402 mono_thread_info_wait_for_resume (info);
403 break;
404 default:
405 g_error ("Unknown thread state");
408 if (info->async_target) {
409 info->async_target (info->user_data);
410 info->async_target = NULL;
411 info->user_data = NULL;
414 W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
417 void
418 mono_threads_exit_gc_safe_region_unbalanced (gpointer cookie, gpointer *stackpointer)
420 MONO_STACKDATA (stackdata);
421 stackdata.stackpointer = stackpointer;
422 mono_threads_exit_gc_safe_region_unbalanced_internal (cookie, &stackdata);
425 void
426 mono_threads_assert_gc_safe_region (void)
428 MONO_REQ_GC_SAFE_MODE;
431 gpointer
432 mono_threads_enter_gc_unsafe_region_internal (MonoStackData *stackdata)
434 return mono_threads_enter_gc_unsafe_region_with_info (mono_thread_info_current_unchecked (), stackdata);
437 gpointer
438 mono_threads_enter_gc_unsafe_region (gpointer *stackpointer)
440 MONO_STACKDATA (stackdata);
441 stackdata.stackpointer = stackpointer;
442 return mono_threads_enter_gc_unsafe_region_internal (&stackdata);
445 gpointer
446 mono_threads_enter_gc_unsafe_region_with_info (THREAD_INFO_TYPE *info, MonoStackData *stackdata)
448 gpointer cookie;
450 if (!mono_threads_is_blocking_transition_enabled ())
451 return NULL;
453 cookie = mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, stackdata);
455 #ifdef ENABLE_CHECKED_BUILD_GC
456 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
457 coop_tls_push (cookie);
458 #endif
460 return cookie;
463 gpointer
464 mono_threads_enter_gc_unsafe_region_unbalanced_internal (MonoStackData *stackdata)
466 return mono_threads_enter_gc_unsafe_region_unbalanced_with_info (mono_thread_info_current_unchecked (), stackdata);
469 gpointer
470 mono_threads_enter_gc_unsafe_region_unbalanced (gpointer *stackpointer)
472 MONO_STACKDATA (stackdata);
473 stackdata.stackpointer = stackpointer;
474 return mono_threads_enter_gc_unsafe_region_unbalanced_internal (&stackdata);
477 gpointer
478 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (MonoThreadInfo *info, MonoStackData *stackdata)
480 if (!mono_threads_is_blocking_transition_enabled ())
481 return NULL;
483 ++coop_reset_blocking_count;
485 const char *function_name = mono_stackdata_get_function_name (stackdata);
487 check_info (info, "enter", "unsafe", function_name);
489 copy_stack_data (info, stackdata);
491 switch (mono_threads_transition_abort_blocking (info, function_name)) {
492 case AbortBlockingIgnore:
493 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
494 return NULL;
495 case AbortBlockingIgnoreAndPoll:
496 mono_threads_state_poll_with_info (info);
497 return NULL;
498 case AbortBlockingOk:
499 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
500 break;
501 case AbortBlockingWait:
502 /* If full coop suspend, we're just waiting for the initiator
503 * to resume us. If hybrid suspend, we were either self
504 * suspended cooperatively from async_suspend_requested (same
505 * as full coop), or we were suspended preemptively while in
506 * blocking and we're waiting for two things: the suspend
507 * signal handler to run and notify the initiator and
508 * immediately return, and then for the resume. */
509 mono_thread_info_wait_for_resume (info);
510 break;
511 default:
512 g_error ("Unknown thread state %s", function_name);
515 if (info->async_target) {
516 info->async_target (info->user_data);
517 info->async_target = NULL;
518 info->user_data = NULL;
521 return info;
524 gpointer
525 mono_threads_enter_gc_unsafe_region_cookie (void)
527 MonoThreadInfo *info;
529 g_assert (mono_threads_is_blocking_transition_enabled ());
531 info = mono_thread_info_current_unchecked ();
533 check_info (info, "enter (cookie)", "unsafe", "");
535 #ifdef ENABLE_CHECKED_BUILD_GC
536 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
537 coop_tls_push (info);
538 #endif
540 return info;
543 void
544 mono_threads_exit_gc_unsafe_region_internal (gpointer cookie, MonoStackData *stackdata)
546 if (!mono_threads_is_blocking_transition_enabled ())
547 return;
549 #ifdef ENABLE_CHECKED_BUILD_GC
550 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
551 coop_tls_pop (cookie);
552 #endif
554 mono_threads_exit_gc_unsafe_region_unbalanced_internal (cookie, stackdata);
557 void
558 mono_threads_exit_gc_unsafe_region (gpointer cookie, gpointer *stackpointer)
560 MONO_STACKDATA (stackdata);
561 stackdata.stackpointer = stackpointer;
562 mono_threads_exit_gc_unsafe_region_internal (cookie, &stackdata);
565 void
566 mono_threads_exit_gc_unsafe_region_unbalanced_internal (gpointer cookie, MonoStackData *stackdata)
568 if (!mono_threads_is_blocking_transition_enabled ())
569 return;
571 if (!cookie)
572 return;
574 mono_threads_enter_gc_safe_region_unbalanced_internal (stackdata);
577 void
578 mono_threads_exit_gc_unsafe_region_unbalanced (gpointer cookie, gpointer *stackpointer)
580 MONO_STACKDATA (stackdata);
581 stackdata.stackpointer = stackpointer;
582 mono_threads_exit_gc_unsafe_region_unbalanced_internal (cookie, &stackdata);
585 void
586 mono_threads_assert_gc_unsafe_region (void)
588 MONO_REQ_GC_UNSAFE_MODE;
591 static int
592 threads_suspend_policy_default (void)
594 #if defined (ENABLE_COOP_SUSPEND)
595 return MONO_THREADS_SUSPEND_FULL_COOP;
596 #elif defined (ENABLE_HYBRID_SUSPEND)
597 return MONO_THREADS_SUSPEND_HYBRID;
598 #else
599 return 0; /* unset */
600 #endif
603 /* Look up whether an env var is set, warn that it's obsolete and offer a new
604 * alternative
606 static gboolean
607 hasenv_obsolete (const char *name, const char* newval)
609 // If they already set MONO_THREADS_SUSPEND to something, maybe they're keeping
610 // the old var set for compatability with old Mono - in that case don't nag.
611 // FIXME: but maybe nag if MONO_THREADS_SUSPEND isn't set to "newval"?
612 static int quiet = -1;
613 if (g_hasenv (name)) {
614 if (G_UNLIKELY (quiet == -1))
615 quiet = g_hasenv ("MONO_THREADS_SUSPEND");
616 if (!quiet)
617 g_warning ("%s environment variable is obsolete. Use MONO_THREADS_SUSPEND=%s", name, newval);
618 return TRUE;
620 return FALSE;
623 static int
624 threads_suspend_policy_getenv_compat (void)
626 int policy = 0;
627 if (hasenv_obsolete ("MONO_ENABLE_COOP", "coop") || hasenv_obsolete ("MONO_ENABLE_COOP_SUSPEND", "coop")) {
628 g_assertf (!hasenv_obsolete ("MONO_ENABLE_HYBRID_SUSPEND", "hybrid"),
629 "Environment variables set to enable both hybrid and cooperative suspend simultaneously");
630 policy = MONO_THREADS_SUSPEND_FULL_COOP;
631 } else if (hasenv_obsolete ("MONO_ENABLE_HYBRID_SUSPEND", "hybrid"))
632 policy = MONO_THREADS_SUSPEND_HYBRID;
633 return policy;
636 static int
637 threads_suspend_policy_getenv (void)
639 int policy = 0;
640 if (g_hasenv ("MONO_THREADS_SUSPEND")) {
641 gchar *str = g_getenv ("MONO_THREADS_SUSPEND");
642 if (!strcmp (str, "coop"))
643 policy = MONO_THREADS_SUSPEND_FULL_COOP;
644 else if (!strcmp (str, "hybrid"))
645 policy = MONO_THREADS_SUSPEND_HYBRID;
646 else if (!strcmp (str, "preemptive"))
647 policy = MONO_THREADS_SUSPEND_FULL_PREEMPTIVE;
648 else
649 g_error ("MONO_THREADS_SUSPEND environment variable set to '%s', must be one of coop, hybrid, preemptive.", str);
650 g_free (str);
652 return policy;
655 static char threads_suspend_policy;
657 MonoThreadsSuspendPolicy
658 mono_threads_suspend_policy (void)
660 int policy = threads_suspend_policy;
661 if (G_UNLIKELY (policy == 0)) {
662 // thread suspend policy:
663 // if the MONO_THREADS_SUSPEND env is set, use it.
664 // otherwise if there's a compiled-in default, use it.
665 // otherwise if one of the old environment variables is set, use that.
666 // otherwise use full preemptive suspend.
668 W32_DEFINE_LAST_ERROR_RESTORE_POINT;
670 (policy = threads_suspend_policy_getenv ())
671 || (policy = threads_suspend_policy_default ())
672 || (policy = threads_suspend_policy_getenv_compat ())
673 || (policy = MONO_THREADS_SUSPEND_FULL_PREEMPTIVE);
675 W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
677 g_assert (policy);
678 threads_suspend_policy = (char)policy;
680 return (MonoThreadsSuspendPolicy)policy;
683 static MonoThreadsSuspendPolicy
684 mono_threads_suspend_validate_policy (MonoThreadsSuspendPolicy policy)
686 switch (policy) {
687 case MONO_THREADS_SUSPEND_FULL_COOP:
688 case MONO_THREADS_SUSPEND_FULL_PREEMPTIVE:
689 case MONO_THREADS_SUSPEND_HYBRID:
690 return policy;
691 default:
692 g_error ("Invalid suspend policy %d.", (int)policy);
697 * mono_threads_suspend_override_policy:
699 * Don't use this. Provides a last resort escape hatch to override configure
700 * and environment settings and use the given thread suspend policy.
703 void
704 mono_threads_suspend_override_policy (MonoThreadsSuspendPolicy new_policy)
706 threads_suspend_policy = (char)mono_threads_suspend_validate_policy (new_policy);
707 g_warning ("Overriding suspend policy. Using %s suspend.", mono_threads_suspend_policy_name (mono_threads_suspend_policy ()));
710 const char*
711 mono_threads_suspend_policy_name (MonoThreadsSuspendPolicy policy)
713 switch (policy) {
714 case MONO_THREADS_SUSPEND_FULL_COOP:
715 return "cooperative";
716 case MONO_THREADS_SUSPEND_FULL_PREEMPTIVE:
717 return "preemptive";
718 case MONO_THREADS_SUSPEND_HYBRID:
719 return "hybrid";
720 default:
721 g_assert_not_reached ();
725 gboolean
726 mono_threads_is_cooperative_suspension_enabled (void)
728 return (mono_threads_suspend_policy () == MONO_THREADS_SUSPEND_FULL_COOP);
731 gboolean
732 mono_threads_is_hybrid_suspension_enabled (void)
734 return (mono_threads_suspend_policy () == MONO_THREADS_SUSPEND_HYBRID);
737 void
738 mono_threads_coop_init (void)
740 if (!mono_threads_are_safepoints_enabled () && !mono_threads_is_blocking_transition_enabled ())
741 return;
743 mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
744 mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
745 mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
746 mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
747 mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
748 //See the above for what's wrong here.
750 #ifdef ENABLE_CHECKED_BUILD_GC
751 mono_native_tls_alloc (&coop_reset_count_stack_key, NULL);
752 #endif
755 void
756 mono_threads_coop_begin_global_suspend (void)
758 if (mono_threads_are_safepoints_enabled ())
759 mono_polling_required = 1;
762 void
763 mono_threads_coop_end_global_suspend (void)
765 if (mono_threads_are_safepoints_enabled ())
766 mono_polling_required = 0;
769 void
770 mono_threads_enter_no_safepoints_region (const char *func)
772 MONO_REQ_GC_UNSAFE_MODE;
773 mono_threads_transition_begin_no_safepoints (mono_thread_info_current (), func);
776 void
777 mono_threads_exit_no_safepoints_region (const char *func)
779 MONO_REQ_GC_UNSAFE_MODE;
780 mono_threads_transition_end_no_safepoints (mono_thread_info_current (), func);
783 void
784 mono_thread_set_coop_aware (void)
786 MONO_ENTER_GC_UNSAFE;
787 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
788 if (info)
789 /* NOTE, this flag should only be changed while in unsafe mode. */
790 /* It will make sure we won't get an async preemptive suspend */
791 /* request against this thread while in the process of changing the flag */
792 /* affecting the threads suspend/resume behavior. */
793 mono_atomic_store_i32 (&(info->coop_aware_thread), TRUE);
794 MONO_EXIT_GC_UNSAFE;
797 mono_bool
798 mono_thread_get_coop_aware (void)
800 mono_bool result = FALSE;
801 MONO_ENTER_GC_UNSAFE;
802 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
803 if (info)
804 result = (mono_bool)mono_atomic_load_i32 (&(info->coop_aware_thread));
805 MONO_EXIT_GC_UNSAFE;
807 return result;