Add logic to object array typecheck to handle arrays of unmanaged pointers (#14733)
[mono-project.git] / mono / utils / mono-threads-coop.c
blob52dfc81378fb80c7a2bdadc549c89faf373e9bbf
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 #ifdef _MSC_VER
38 // __builtin_unwind_init not available under MSVC but equivalent implementation is done using
39 // copy_stack_data_internal_win32_wrapper.
40 #define SAVE_REGS_ON_STACK do {} while (0)
41 #elif defined (HOST_WASM)
42 //TODO: figure out wasm stack scanning
43 #define SAVE_REGS_ON_STACK do {} while (0)
44 #else
45 #define SAVE_REGS_ON_STACK __builtin_unwind_init ();
46 #endif
48 volatile size_t mono_polling_required;
50 // FIXME: This would be more efficient if instead of instantiating the stack it just pushed a simple depth counter up and down,
51 // perhaps with a per-thread cookie in the high bits.
52 #ifdef ENABLE_CHECKED_BUILD_GC
54 // Maintains a single per-thread stack of ints, used to ensure nesting is not violated
55 static MonoNativeTlsKey coop_reset_count_stack_key;
57 static void
58 coop_tls_push (gpointer cookie)
60 GArray *stack;
62 stack = (GArray*)mono_native_tls_get_value (coop_reset_count_stack_key);
63 if (!stack) {
64 stack = g_array_new (FALSE, FALSE, sizeof(gpointer));
65 mono_native_tls_set_value (coop_reset_count_stack_key, stack);
68 g_array_append_val (stack, cookie);
71 static void
72 coop_tls_pop (gpointer received_cookie)
74 GArray *stack;
75 gpointer expected_cookie;
77 stack = (GArray*)mono_native_tls_get_value (coop_reset_count_stack_key);
78 if (!stack || 0 == stack->len)
79 mono_fatal_with_history ("Received cookie %p but found no stack at all\n", received_cookie);
81 expected_cookie = g_array_index (stack, gpointer, stack->len - 1);
82 stack->len --;
84 if (0 == stack->len) {
85 g_array_free (stack,TRUE);
86 mono_native_tls_set_value (coop_reset_count_stack_key, NULL);
89 if (expected_cookie != received_cookie)
90 mono_fatal_with_history ("Received cookie %p but expected %p\n", received_cookie, expected_cookie);
93 #endif
95 static void
96 check_info (MonoThreadInfo *info, const gchar *action, const gchar *state, const char *func)
98 if (!info)
99 g_error ("%s Cannot %s GC %s region if the thread is not attached", func, action, state);
100 if (!mono_thread_info_is_current (info))
101 g_error ("%s [%p] Cannot %s GC %s region on a different thread", func, mono_thread_info_get_tid (info), action, state);
102 if (!mono_thread_info_is_live (info))
103 g_error ("%s [%p] Cannot %s GC %s region if the thread is not live", func, mono_thread_info_get_tid (info), action, state);
106 static int coop_reset_blocking_count;
107 static int coop_try_blocking_count;
108 static int coop_do_blocking_count;
109 static int coop_do_polling_count;
110 static int coop_save_count;
112 static void
113 mono_threads_state_poll_with_info (MonoThreadInfo *info);
115 void
116 mono_threads_state_poll (void)
118 mono_threads_state_poll_with_info (mono_thread_info_current_unchecked ());
121 static void
122 mono_threads_state_poll_with_info (MonoThreadInfo *info)
124 g_assert (mono_threads_is_blocking_transition_enabled ());
126 ++coop_do_polling_count;
128 if (!info)
129 return;
131 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
133 /* Fast fail if no_safepoints is set */
134 g_assert (!(info->thread_state & THREAD_SUSPEND_NO_SAFEPOINTS_MASK));
136 /* Fast check for pending suspend requests */
137 if (!(info->thread_state & STATE_ASYNC_SUSPEND_REQUESTED))
138 return;
140 ++coop_save_count;
141 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
143 /* commit the saved state and notify others if needed */
144 switch (mono_threads_transition_state_poll (info)) {
145 case SelfSuspendResumed:
146 break;
147 case SelfSuspendNotifyAndWait:
148 mono_threads_notify_initiator_of_suspend (info);
149 mono_thread_info_wait_for_resume (info);
150 break;
153 if (info->async_target) {
154 info->async_target (info->user_data);
155 info->async_target = NULL;
156 info->user_data = NULL;
160 static volatile gpointer* dummy_global;
162 static MONO_NEVER_INLINE
163 void*
164 return_stack_ptr (gpointer *i)
166 dummy_global = i;
167 return i;
170 static void
171 copy_stack_data_internal (MonoThreadInfo *info, MonoStackData *stackdata_begin, gconstpointer wrapper_data1, gconstpointer wrapper_data2)
173 MonoThreadUnwindState *state;
174 int stackdata_size;
175 gpointer dummy;
176 void* stackdata_end = return_stack_ptr (&dummy);
178 SAVE_REGS_ON_STACK;
180 state = &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
182 stackdata_size = (char*)mono_stackdata_get_stackpointer (stackdata_begin) - (char*)stackdata_end;
184 const char *function_name = mono_stackdata_get_function_name (stackdata_begin);
186 if (((gsize) stackdata_begin & (SIZEOF_VOID_P - 1)) != 0)
187 g_error ("%s stackdata_begin (%p) must be %d-byte aligned", function_name, stackdata_begin, SIZEOF_VOID_P);
188 if (((gsize) stackdata_end & (SIZEOF_VOID_P - 1)) != 0)
189 g_error ("%s stackdata_end (%p) must be %d-byte aligned", function_name, stackdata_end, SIZEOF_VOID_P);
191 if (stackdata_size <= 0)
192 g_error ("%s stackdata_size = %d, but must be > 0, stackdata_begin = %p, stackdata_end = %p", function_name, stackdata_size, stackdata_begin, stackdata_end);
194 g_byte_array_set_size (info->stackdata, stackdata_size);
195 state->gc_stackdata = info->stackdata->data;
196 memcpy (state->gc_stackdata, stackdata_end, stackdata_size);
198 state->gc_stackdata_size = stackdata_size;
201 #ifdef _MSC_VER
202 typedef void (*CopyStackDataFunc)(MonoThreadInfo *, MonoStackData *, gconstpointer, gconstpointer);
204 #ifdef TARGET_AMD64
205 // Implementation of __builtin_unwind_init under MSVC, dumping nonvolatile registers into MonoBuiltinUnwindInfo.
206 typedef struct {
207 __m128d fregs [10];
208 host_mgreg_t gregs [8];
209 } MonoBuiltinUnwindInfo;
211 // Defined in win64.asm
212 G_EXTERN_C void
213 copy_stack_data_internal_win32_wrapper (MonoThreadInfo *, MonoStackData *, MonoBuiltinUnwindInfo *, CopyStackDataFunc);
214 #else
215 // Implementation of __builtin_unwind_init under MSVC, dumping nonvolatile registers into MonoBuiltinUnwindInfo.
216 typedef struct {
217 host_mgreg_t gregs [4];
218 } MonoBuiltinUnwindInfo;
220 // Implementation of __builtin_unwind_init under MSVC, dumping nonvolatile registers into MonoBuiltinUnwindInfo *.
221 __declspec(naked) void __cdecl
222 copy_stack_data_internal_win32_wrapper (MonoThreadInfo *info, MonoStackData *stackdata_begin, MonoBuiltinUnwindInfo *unwind_info_data, CopyStackDataFunc func)
224 __asm {
225 mov edx, dword ptr [esp + 0Ch]
226 mov dword ptr [edx + 00h], ebx
227 mov dword ptr [edx + 04h], esi
228 mov dword ptr [edx + 08h], edi
229 mov dword ptr [edx + 0Ch], ebp
231 // tailcall, all parameters passed through to CopyStackDataFunc.
232 mov edx, dword ptr [esp + 10h]
233 jmp edx
236 #endif
238 static void
239 copy_stack_data (MonoThreadInfo *info, MonoStackData *stackdata_begin)
241 MonoBuiltinUnwindInfo unwind_info_data;
242 copy_stack_data_internal_win32_wrapper (info, stackdata_begin, &unwind_info_data, copy_stack_data_internal);
244 #else
245 static void
246 copy_stack_data (MonoThreadInfo *info, MonoStackData *stackdata_begin)
248 copy_stack_data_internal (info, stackdata_begin, NULL, NULL);
250 #endif
252 static gpointer
253 mono_threads_enter_gc_safe_region_unbalanced_with_info (MonoThreadInfo *info, MonoStackData *stackdata);
255 gpointer
256 mono_threads_enter_gc_safe_region_internal (MonoStackData *stackdata)
258 return mono_threads_enter_gc_safe_region_with_info (mono_thread_info_current_unchecked (), stackdata);
261 gpointer
262 mono_threads_enter_gc_safe_region (gpointer *stackpointer)
264 MONO_STACKDATA (stackdata);
265 stackdata.stackpointer = stackpointer;
266 return mono_threads_enter_gc_safe_region_internal (&stackdata);
269 gpointer
270 mono_threads_enter_gc_safe_region_with_info (MonoThreadInfo *info, MonoStackData *stackdata)
272 gpointer cookie;
274 if (!mono_threads_is_blocking_transition_enabled ())
275 return NULL;
277 cookie = mono_threads_enter_gc_safe_region_unbalanced_with_info (info, stackdata);
279 #ifdef ENABLE_CHECKED_BUILD_GC
280 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
281 coop_tls_push (cookie);
282 #endif
284 return cookie;
287 gpointer
288 mono_threads_enter_gc_safe_region_unbalanced_internal (MonoStackData *stackdata)
290 return mono_threads_enter_gc_safe_region_unbalanced_with_info (mono_thread_info_current_unchecked (), stackdata);
293 gpointer
294 mono_threads_enter_gc_safe_region_unbalanced (gpointer *stackpointer)
296 MONO_STACKDATA (stackdata);
297 stackdata.stackpointer = stackpointer;
298 return mono_threads_enter_gc_safe_region_unbalanced_internal (&stackdata);
301 static gpointer
302 mono_threads_enter_gc_safe_region_unbalanced_with_info (MonoThreadInfo *info, MonoStackData *stackdata)
304 if (!mono_threads_is_blocking_transition_enabled ())
305 return NULL;
307 ++coop_do_blocking_count;
309 const char *function_name = mono_stackdata_get_function_name (stackdata);
311 check_info (info, "enter", "safe", function_name);
313 copy_stack_data (info, stackdata);
315 retry:
316 ++coop_save_count;
317 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
319 switch (mono_threads_transition_do_blocking (info, function_name)) {
320 case DoBlockingContinue:
321 break;
322 case DoBlockingPollAndRetry:
323 mono_threads_state_poll_with_info (info);
324 goto retry;
327 return info;
330 void
331 mono_threads_exit_gc_safe_region_internal (gpointer cookie, MonoStackData *stackdata)
333 if (!mono_threads_is_blocking_transition_enabled ())
334 return;
336 #ifdef ENABLE_CHECKED_BUILD_GC
337 W32_DEFINE_LAST_ERROR_RESTORE_POINT;
338 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
339 coop_tls_pop (cookie);
340 W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
341 #endif
343 mono_threads_exit_gc_safe_region_unbalanced_internal (cookie, stackdata);
346 void
347 mono_threads_exit_gc_safe_region (gpointer cookie, gpointer *stackpointer)
349 MONO_STACKDATA (stackdata);
350 stackdata.stackpointer = stackpointer;
351 mono_threads_exit_gc_safe_region_internal (cookie, &stackdata);
354 void
355 mono_threads_exit_gc_safe_region_unbalanced_internal (gpointer cookie, MonoStackData *stackdata)
357 MonoThreadInfo *info;
359 if (!mono_threads_is_blocking_transition_enabled ())
360 return;
362 /* Common to use enter/exit gc safe around OS API's affecting last error. */
363 /* This method can call OS API's that will reset last error on some platforms. */
364 /* To reduce errors, we need to restore last error before exit gc safe. */
365 W32_DEFINE_LAST_ERROR_RESTORE_POINT;
367 info = (MonoThreadInfo *)cookie;
369 const char *function_name = mono_stackdata_get_function_name (stackdata);
371 check_info (info, "exit", "safe", function_name);
373 switch (mono_threads_transition_done_blocking (info, function_name)) {
374 case DoneBlockingOk:
375 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
376 break;
377 case DoneBlockingWait:
378 /* If full coop suspend, we're just waiting for the initiator
379 * to resume us. If hybrid suspend, we were either self
380 * suspended cooperatively from async_suspend_requested (same
381 * as full coop), or we were suspended preemptively while in
382 * blocking and we're waiting for two things: the suspend
383 * signal handler to run and notify the initiator and
384 * immediately return, and then for the resume. */
385 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
386 mono_thread_info_wait_for_resume (info);
387 break;
388 default:
389 g_error ("Unknown thread state");
392 if (info->async_target) {
393 info->async_target (info->user_data);
394 info->async_target = NULL;
395 info->user_data = NULL;
398 W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
401 void
402 mono_threads_exit_gc_safe_region_unbalanced (gpointer cookie, gpointer *stackpointer)
404 MONO_STACKDATA (stackdata);
405 stackdata.stackpointer = stackpointer;
406 mono_threads_exit_gc_safe_region_unbalanced_internal (cookie, &stackdata);
409 void
410 mono_threads_assert_gc_safe_region (void)
412 MONO_REQ_GC_SAFE_MODE;
415 gpointer
416 mono_threads_enter_gc_unsafe_region_internal (MonoStackData *stackdata)
418 return mono_threads_enter_gc_unsafe_region_with_info (mono_thread_info_current_unchecked (), stackdata);
421 gpointer
422 mono_threads_enter_gc_unsafe_region (gpointer *stackpointer)
424 MONO_STACKDATA (stackdata);
425 stackdata.stackpointer = stackpointer;
426 return mono_threads_enter_gc_unsafe_region_internal (&stackdata);
429 gpointer
430 mono_threads_enter_gc_unsafe_region_with_info (THREAD_INFO_TYPE *info, MonoStackData *stackdata)
432 gpointer cookie;
434 if (!mono_threads_is_blocking_transition_enabled ())
435 return NULL;
437 cookie = mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, stackdata);
439 #ifdef ENABLE_CHECKED_BUILD_GC
440 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
441 coop_tls_push (cookie);
442 #endif
444 return cookie;
447 gpointer
448 mono_threads_enter_gc_unsafe_region_unbalanced_internal (MonoStackData *stackdata)
450 return mono_threads_enter_gc_unsafe_region_unbalanced_with_info (mono_thread_info_current_unchecked (), stackdata);
453 gpointer
454 mono_threads_enter_gc_unsafe_region_unbalanced (gpointer *stackpointer)
456 MONO_STACKDATA (stackdata);
457 stackdata.stackpointer = stackpointer;
458 return mono_threads_enter_gc_unsafe_region_unbalanced_internal (&stackdata);
461 gpointer
462 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (MonoThreadInfo *info, MonoStackData *stackdata)
464 if (!mono_threads_is_blocking_transition_enabled ())
465 return NULL;
467 ++coop_reset_blocking_count;
469 const char *function_name = mono_stackdata_get_function_name (stackdata);
471 check_info (info, "enter", "unsafe", function_name);
473 copy_stack_data (info, stackdata);
475 switch (mono_threads_transition_abort_blocking (info, function_name)) {
476 case AbortBlockingIgnore:
477 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
478 return NULL;
479 case AbortBlockingIgnoreAndPoll:
480 mono_threads_state_poll_with_info (info);
481 return NULL;
482 case AbortBlockingOk:
483 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
484 break;
485 case AbortBlockingWait:
486 /* If full coop suspend, we're just waiting for the initiator
487 * to resume us. If hybrid suspend, we were either self
488 * suspended cooperatively from async_suspend_requested (same
489 * as full coop), or we were suspended preemptively while in
490 * blocking and we're waiting for two things: the suspend
491 * signal handler to run and notify the initiator and
492 * immediately return, and then for the resume. */
493 mono_thread_info_wait_for_resume (info);
494 break;
495 default:
496 g_error ("Unknown thread state %s", function_name);
499 if (info->async_target) {
500 info->async_target (info->user_data);
501 info->async_target = NULL;
502 info->user_data = NULL;
505 return info;
508 gpointer
509 mono_threads_enter_gc_unsafe_region_cookie (void)
511 MonoThreadInfo *info;
513 g_assert (mono_threads_is_blocking_transition_enabled ());
515 info = mono_thread_info_current_unchecked ();
517 check_info (info, "enter (cookie)", "unsafe", "");
519 #ifdef ENABLE_CHECKED_BUILD_GC
520 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
521 coop_tls_push (info);
522 #endif
524 return info;
527 void
528 mono_threads_exit_gc_unsafe_region_internal (gpointer cookie, MonoStackData *stackdata)
530 if (!mono_threads_is_blocking_transition_enabled ())
531 return;
533 #ifdef ENABLE_CHECKED_BUILD_GC
534 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
535 coop_tls_pop (cookie);
536 #endif
538 mono_threads_exit_gc_unsafe_region_unbalanced_internal (cookie, stackdata);
541 void
542 mono_threads_exit_gc_unsafe_region (gpointer cookie, gpointer *stackpointer)
544 MONO_STACKDATA (stackdata);
545 stackdata.stackpointer = stackpointer;
546 mono_threads_exit_gc_unsafe_region_internal (cookie, &stackdata);
549 void
550 mono_threads_exit_gc_unsafe_region_unbalanced_internal (gpointer cookie, MonoStackData *stackdata)
552 if (!mono_threads_is_blocking_transition_enabled ())
553 return;
555 if (!cookie)
556 return;
558 mono_threads_enter_gc_safe_region_unbalanced_internal (stackdata);
561 void
562 mono_threads_exit_gc_unsafe_region_unbalanced (gpointer cookie, gpointer *stackpointer)
564 MONO_STACKDATA (stackdata);
565 stackdata.stackpointer = stackpointer;
566 mono_threads_exit_gc_unsafe_region_unbalanced_internal (cookie, &stackdata);
569 void
570 mono_threads_assert_gc_unsafe_region (void)
572 MONO_REQ_GC_UNSAFE_MODE;
575 static int
576 threads_suspend_policy_default (void)
578 #if defined (ENABLE_COOP_SUSPEND)
579 return MONO_THREADS_SUSPEND_FULL_COOP;
580 #elif defined (ENABLE_HYBRID_SUSPEND)
581 return MONO_THREADS_SUSPEND_HYBRID;
582 #else
583 return 0; /* unset */
584 #endif
587 /* Look up whether an env var is set, warn that it's obsolete and offer a new
588 * alternative
590 static gboolean
591 hasenv_obsolete (const char *name, const char* newval)
593 // If they already set MONO_THREADS_SUSPEND to something, maybe they're keeping
594 // the old var set for compatability with old Mono - in that case don't nag.
595 // FIXME: but maybe nag if MONO_THREADS_SUSPEND isn't set to "newval"?
596 static int quiet = -1;
597 if (g_hasenv (name)) {
598 if (G_UNLIKELY (quiet == -1))
599 quiet = g_hasenv ("MONO_THREADS_SUSPEND");
600 if (!quiet)
601 g_warning ("%s environment variable is obsolete. Use MONO_THREADS_SUSPEND=%s", name, newval);
602 return TRUE;
604 return FALSE;
607 static int
608 threads_suspend_policy_getenv_compat (void)
610 int policy = 0;
611 if (hasenv_obsolete ("MONO_ENABLE_COOP", "coop") || hasenv_obsolete ("MONO_ENABLE_COOP_SUSPEND", "coop")) {
612 g_assertf (!hasenv_obsolete ("MONO_ENABLE_HYBRID_SUSPEND", "hybrid"),
613 "Environment variables set to enable both hybrid and cooperative suspend simultaneously");
614 policy = MONO_THREADS_SUSPEND_FULL_COOP;
615 } else if (hasenv_obsolete ("MONO_ENABLE_HYBRID_SUSPEND", "hybrid"))
616 policy = MONO_THREADS_SUSPEND_HYBRID;
617 return policy;
620 static int
621 threads_suspend_policy_getenv (void)
623 int policy = 0;
624 if (g_hasenv ("MONO_THREADS_SUSPEND")) {
625 gchar *str = g_getenv ("MONO_THREADS_SUSPEND");
626 if (!strcmp (str, "coop"))
627 policy = MONO_THREADS_SUSPEND_FULL_COOP;
628 else if (!strcmp (str, "hybrid"))
629 policy = MONO_THREADS_SUSPEND_HYBRID;
630 else if (!strcmp (str, "preemptive"))
631 policy = MONO_THREADS_SUSPEND_FULL_PREEMPTIVE;
632 else
633 g_error ("MONO_THREADS_SUSPEND environment variable set to '%s', must be one of coop, hybrid, preemptive.", str);
634 g_free (str);
636 return policy;
639 static char threads_suspend_policy;
641 MonoThreadsSuspendPolicy
642 mono_threads_suspend_policy (void)
644 int policy = threads_suspend_policy;
645 if (G_UNLIKELY (policy == 0)) {
646 // thread suspend policy:
647 // if the MONO_THREADS_SUSPEND env is set, use it.
648 // otherwise if there's a compiled-in default, use it.
649 // otherwise if one of the old environment variables is set, use that.
650 // otherwise use full preemptive suspend.
652 W32_DEFINE_LAST_ERROR_RESTORE_POINT;
654 (policy = threads_suspend_policy_getenv ())
655 || (policy = threads_suspend_policy_default ())
656 || (policy = threads_suspend_policy_getenv_compat ())
657 || (policy = MONO_THREADS_SUSPEND_FULL_PREEMPTIVE);
659 W32_RESTORE_LAST_ERROR_FROM_RESTORE_POINT;
661 g_assert (policy);
662 threads_suspend_policy = (char)policy;
664 return (MonoThreadsSuspendPolicy)policy;
667 static MonoThreadsSuspendPolicy
668 mono_threads_suspend_validate_policy (MonoThreadsSuspendPolicy policy)
670 switch (policy) {
671 case MONO_THREADS_SUSPEND_FULL_COOP:
672 case MONO_THREADS_SUSPEND_FULL_PREEMPTIVE:
673 case MONO_THREADS_SUSPEND_HYBRID:
674 return policy;
675 default:
676 g_error ("Invalid suspend policy %d.", (int)policy);
681 * mono_threads_suspend_override_policy:
683 * Don't use this. Provides a last resort escape hatch to override configure
684 * and environment settings and use the given thread suspend policy.
687 void
688 mono_threads_suspend_override_policy (MonoThreadsSuspendPolicy new_policy)
690 threads_suspend_policy = (char)mono_threads_suspend_validate_policy (new_policy);
691 g_warning ("Overriding suspend policy. Using %s suspend.", mono_threads_suspend_policy_name (mono_threads_suspend_policy ()));
694 const char*
695 mono_threads_suspend_policy_name (MonoThreadsSuspendPolicy policy)
697 switch (policy) {
698 case MONO_THREADS_SUSPEND_FULL_COOP:
699 return "cooperative";
700 case MONO_THREADS_SUSPEND_FULL_PREEMPTIVE:
701 return "preemptive";
702 case MONO_THREADS_SUSPEND_HYBRID:
703 return "hybrid";
704 default:
705 g_assert_not_reached ();
709 gboolean
710 mono_threads_is_cooperative_suspension_enabled (void)
712 return (mono_threads_suspend_policy () == MONO_THREADS_SUSPEND_FULL_COOP);
715 gboolean
716 mono_threads_is_hybrid_suspension_enabled (void)
718 return (mono_threads_suspend_policy () == MONO_THREADS_SUSPEND_HYBRID);
721 void
722 mono_threads_coop_init (void)
724 if (!mono_threads_are_safepoints_enabled () && !mono_threads_is_blocking_transition_enabled ())
725 return;
727 mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
728 mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
729 mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
730 mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
731 mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
732 //See the above for what's wrong here.
734 #ifdef ENABLE_CHECKED_BUILD_GC
735 mono_native_tls_alloc (&coop_reset_count_stack_key, NULL);
736 #endif
739 void
740 mono_threads_coop_begin_global_suspend (void)
742 if (mono_threads_are_safepoints_enabled ())
743 mono_polling_required = 1;
746 void
747 mono_threads_coop_end_global_suspend (void)
749 if (mono_threads_are_safepoints_enabled ())
750 mono_polling_required = 0;
753 void
754 mono_threads_enter_no_safepoints_region (const char *func)
756 MONO_REQ_GC_UNSAFE_MODE;
757 mono_threads_transition_begin_no_safepoints (mono_thread_info_current (), func);
760 void
761 mono_threads_exit_no_safepoints_region (const char *func)
763 MONO_REQ_GC_UNSAFE_MODE;
764 mono_threads_transition_end_no_safepoints (mono_thread_info_current (), func);