#endif
#endif
- mono_get_eh_callbacks ()->mono_walk_stack_with_state (print_stack_frame_to_string, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, text);
+ mono_get_eh_callbacks ()->mono_walk_stack_with_state (print_stack_frame_to_string, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, text);
mono_thread_info_finish_suspend_and_resume (info);
fprintf (stdout, "%s", text->str);
MonoThreadInfo *info = mono_thread_info_current ();
MonoException *exc = mono_thread_execute_interruption (mono_thread_internal_current ());
if (exc) /*We must use _with_context since we didn't trampoline into the runtime*/
- mono_raise_exception_with_context (exc, &info->suspend_state.ctx);
+ mono_raise_exception_with_context (exc, &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx); /* FIXME using thread_saved_state [ASYNC_SUSPEND_STATE_INDEX] can race with another suspend coming in. */
g_assert_not_reached (); /*this MUST not happen since we can't resume from an async call*/
}
MonoJitInfo *ji = NULL;
if (!info)
return NULL;
- mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, &info->suspend_state, MONO_UNWIND_SIGNAL_SAFE, &ji);
+ mono_get_eh_callbacks ()->mono_walk_stack_with_state (last_managed, mono_thread_info_get_suspend_state (info), MONO_UNWIND_SIGNAL_SAFE, &ji);
return ji;
}
return;
}
- if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (&info->suspend_state)) {
+ if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info))) {
mono_thread_info_finish_suspend_and_resume (info);
return;
}
ji = mono_thread_info_get_last_managed (info);
protected_wrapper = ji && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
- running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
+ running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
if (!protected_wrapper && running_managed) {
/*We are in managed code*/
ji = mono_thread_info_get_last_managed (info);
protected_wrapper = ji && mono_threads_is_critical_method (mono_jit_info_get_method (ji));
- running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
+ running_managed = mono_jit_info_match (ji, MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx));
if (running_managed && !protected_wrapper) {
transition_to_suspended (thread, info);
"SELF_SUSPENDED",
"ASYNC_SUSPEND_REQUESTED",
"SELF_SUSPEND_REQUESTED",
- "SUSPEND_IN_PROGRESS",
- "SUSPEND_PROMOTED_TO_ASYNC",
};
return state_names [get_thread_state (state)];
}
case STATE_SELF_SUSPENDED:
case STATE_ASYNC_SUSPEND_REQUESTED:
case STATE_SELF_SUSPEND_REQUESTED:
- case STATE_SUSPEND_IN_PROGRESS:
- case STATE_SUSPEND_PROMOTED_TO_ASYNC:
g_assert (suspend_count > 0);
break;
default:
STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
STATE_SELF_SUSPENDED: Code should not be running while suspended.
STATE_SELF_SUSPEND_REQUESTED: This is a bug in the self suspend code that didn't execute the second part of it
-STATE_SUSPEND_IN_PROGRESS: This is an internal state of suspension
-STATE_SUSPEND_PROMOTED_TO_ASYNC: This is an internal state of suspension
*/
default:
g_error ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
STATE_SELF_SUSPENDED: Code should not be running while suspended.
STATE_SELF_SUSPEND_REQUESTED: Self suspends should not nest as begin/end should be paired. [1]
-STATE_SUSPEND_IN_PROGRESS: This in an internal state of the self suspend finish protocol. A new self suspend request must not happen during it
-STATE_SUSPEND_PROMOTED_TO_ASYNC: This in an internal state of the self suspend finish protocol. A new self suspend request must not happen during it
[1] This won't trap this sequence of requests: self suspend, async suspend and self suspend.
If this turns to be an issue we can introduce a new suspend request state for when both have been requested.
if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, suspend_count + 1), raw_state) != raw_state)
goto retry_state_change;
trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
- return AsyncSuspendInitSuspend; //This is the first async suspend request against the target [1]
-
- case STATE_SUSPEND_IN_PROGRESS: //Self suspend has already initiated, we need to tell it to inform us in the end
- g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
- g_assert (info != mono_thread_info_current ()); // if this is a self suspend request, which can't happen in this state, as this is the middle of the self suspend protocol.
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SUSPEND_PROMOTED_TO_ASYNC, suspend_count + 1), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_SUSPEND_PROMOTED_TO_ASYNC, 1);
- return AsyncSuspendWait; //This is a self suspend in progress that now needs to notify the initiator
- break;
+ return AsyncSuspendWait; //This is the first async suspend request, change the thread and let it notify us [1]
/*
[1] It's questionable on what to do if we hit the beginning of a self suspend.
The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
-OTOH, an async suspend will speed this and could lead to this happening sooner. This is not set in stone, so we can back out from the current behavior if it shows
-to be a problem.
-
STATE_ASYNC_SUSPEND_REQUESTED: Since there can only be one async suspend in progress and it must finish, it should not be possible to witness this.
-STATE_SUSPEND_PROMOTED_TO_ASYNC: This is a self suspend that was promoted to an async suspend, which should not be possible to witness due to async suspends happening one at a time.
*/
default:
g_error ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", info, state_name (cur_state));
/*
Check the current state of the thread and try to init a self suspend.
+This must be called with self state saved.
+
+Returns one of the following values:
-Returns TRUE is self suspend should start.
+- Resumed: Async resume happened and current thread should keep running
+- Suspend: Caller should wait for a resume signal
+- SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
+ suspend should start.
*/
-gboolean
+MonoSelfSupendResult
mono_threads_transition_state_poll (MonoThreadInfo *info)
{
int raw_state, cur_state, suspend_count;
case STATE_RUNNING:
g_assert (suspend_count == 0);
trace_state_change ("STATE_POLL", info, raw_state, cur_state, 0);
- return FALSE; //We're fine, don't suspend
+ return SelfSuspendResumed; //We're fine, don't suspend
case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
- g_assert (suspend_count > 0);
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SUSPEND_PROMOTED_TO_ASYNC, suspend_count), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("STATE_POLL", info, raw_state, STATE_SUSPEND_PROMOTED_TO_ASYNC, 0);
- return TRUE;
-
case STATE_SELF_SUSPEND_REQUESTED: //Start the self suspend process
g_assert (suspend_count > 0);
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SUSPEND_IN_PROGRESS, suspend_count), raw_state) != raw_state)
+ if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
goto retry_state_change;
- trace_state_change ("STATE_POLL", info, raw_state, STATE_SUSPEND_IN_PROGRESS, 0);
- return TRUE;
+ trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, 0);
+ if (cur_state == STATE_SELF_SUSPEND_REQUESTED)
+ return SelfSuspendWait; //Caller should wait for resume
+ else
+ return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
/*
STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
STATE_SELF_SUSPENDED: Code should not be running while suspended.
-STATE_SUSPEND_IN_PROGRESS: State polling should not happen while self suspend is finishing
-STATE_SUSPEND_PROMOTED_TO_ASYNC: State polling should not happen while self suspend is finishing
*/
default:
g_error ("Cannot transition thread %p from %s with STATE_POLL", info, state_name (cur_state));
}
case STATE_SELF_SUSPEND_REQUESTED: //Self suspend was requested but another thread decided to resume it.
- case STATE_SUSPEND_IN_PROGRESS: //Self suspend is in progress but another thread decided to resume it. [4]
+ // case STATE_SUSPEND_IN_PROGRESS: //Self suspend is in progress but another thread decided to resume it. [4]
g_assert (suspend_count > 0);
if (suspend_count > 1) {
if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
}
}
-/*
-Last part of the self suspend protocol.
-
-This must only happens in the context of a self suspend request. This means that the thread must be on one of the
-valid self suspend states.
-
-Returns one of the following values:
-
-- Resumed: Async resume happened and current thread should keep running
-- Suspend: Caller should wait for a resume signal
-- SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
-*/
-MonoSelfSupendResult
-mono_threads_transition_finish_self_suspend (MonoThreadInfo* info)
-{
- int raw_state, cur_state, suspend_count;
- g_assert (info == mono_thread_info_current ());
-
-retry_state_change:
- UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
- switch (cur_state) {
- case STATE_RUNNING: //An async resume hit us and we should keep running
- trace_state_change ("FINISH_SELF_SUSPEND", info, raw_state, cur_state, 0);
- return SelfSuspendResumed; //Caller should not suspend
-
- case STATE_SUSPEND_IN_PROGRESS: //Self suspend finished
- case STATE_SUSPEND_PROMOTED_TO_ASYNC: //Async suspend happened during the second step of self suspend so the caller needs to notify the initiator.
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("FINISH_SELF_SUSPEND", info, raw_state, STATE_SELF_SUSPENDED, 0);
- if (cur_state == STATE_SUSPEND_IN_PROGRESS)
- return SelfSuspendWait; //Caller should wait for resume
- else
- return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
-/*
-STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
-STATE_SELF_SUSPENDED: Code should not be running while suspended.
-STATE_ASYNC_SUSPEND_REQUESTED: This state should one be witnessed by the state poll transition
-STATE_SELF_SUSPEND_REQUESTED: This state should one be witnessed by the state poll transition
-*/
- default:
- g_error ("Cannot transition thread %p from %s with FINISH_SELF_SUSPEND", info, state_name (cur_state));
-
- }
-}
-
/*
This performs the last step of async suspend.
goto retry_state_change;
trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, 0);
return TRUE; //Async suspend worked, now wait for resume
-
- case STATE_SUSPEND_IN_PROGRESS:
- if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SUSPEND_PROMOTED_TO_ASYNC, suspend_count), raw_state) != raw_state)
- goto retry_state_change;
- trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_SUSPEND_PROMOTED_TO_ASYNC, 0);
- return FALSE; //async suspend race with self suspend and lost, let the other finish it
+ //
+ // case STATE_SUSPEND_IN_PROGRESS:
+ // if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SUSPEND_PROMOTED_TO_ASYNC, suspend_count), raw_state) != raw_state)
+ // goto retry_state_change;
+ // trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_SUSPEND_PROMOTED_TO_ASYNC, 0);
+ // return FALSE; //async suspend race with self suspend and lost, let the other finish it
/*
STATE_RUNNING: A thread cannot escape suspension once requested.
STATE_ASYNC_SUSPENDED: There can be only one suspend initiator at a given time, meaning this state should have been visible on the first stage of suspend.
}
}
+MonoThreadUnwindState*
+mono_thread_info_get_suspend_state (MonoThreadInfo *info)
+{
+ int raw_state, cur_state, suspend_count;
+ UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
+ switch (cur_state) {
+ case STATE_ASYNC_SUSPENDED:
+ return &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX];
+ case STATE_SELF_SUSPENDED:
+ return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
+ default:
+ g_error ("Cannot read suspend state when the target is in the %s state", state_name (cur_state));
+ }
+}
STATE_SELF_SUSPENDED = 0x04,
STATE_ASYNC_SUSPEND_REQUESTED = 0x05,
STATE_SELF_SUSPEND_REQUESTED = 0x06,
- STATE_SUSPEND_IN_PROGRESS = 0x07,
- STATE_SUSPEND_PROMOTED_TO_ASYNC = 0x08,
- STATE_MAX = 0x08,
+ // STATE_SUSPEND_IN_PROGRESS = 0x07,
+ // STATE_SUSPEND_PROMOTED_TO_ASYNC = 0x08,
+ STATE_MAX = 0x06,
THREAD_STATE_MASK = 0x00FF,
THREAD_SUSPEND_COUNT_MASK = 0xFF00,
THREAD_SUSPEND_COUNT_SHIFT = 8,
- THREAD_SUSPEND_COUNT_MAX = 0xFF
+ THREAD_SUSPEND_COUNT_MAX = 0xFF,
+
+ SELF_SUSPEND_STATE_INDEX = 0,
+ ASYNC_SUSPEND_STATE_INDEX = 1,
};
/*
#endif
/*In theory, only the posix backend needs this, but having it on mach/win32 simplifies things a lot.*/
- MonoThreadUnwindState suspend_state;
+ MonoThreadUnwindState thread_saved_state [2]; //0 is self suspend, 1 is async suspend.
/*async call machinery, thread MUST be suspended before accessing those fields*/
void (*async_target)(void*);
gboolean mono_threads_transition_detach (THREAD_INFO_TYPE *info);
void mono_threads_transition_request_self_suspension (THREAD_INFO_TYPE *info);
MonoRequestAsyncSuspendResult mono_threads_transition_request_async_suspension (THREAD_INFO_TYPE *info);
-gboolean mono_threads_transition_state_poll (THREAD_INFO_TYPE *info);
+MonoSelfSupendResult mono_threads_transition_state_poll (THREAD_INFO_TYPE *info);
MonoResumeResult mono_threads_transition_request_resume (THREAD_INFO_TYPE* info);
-MonoSelfSupendResult mono_threads_transition_finish_self_suspend (THREAD_INFO_TYPE* info);
gboolean mono_threads_transition_finish_async_suspend (THREAD_INFO_TYPE* info);
-void mono_threads_transition_async_suspend_compensation (MonoThreadInfo* info);
+void mono_threads_transition_async_suspend_compensation (THREAD_INFO_TYPE* info);
+MonoThreadUnwindState* mono_thread_info_get_suspend_state (THREAD_INFO_TYPE *info);
/* Advanced suspend API, used for suspending multiple threads as once. */
gboolean mono_thread_info_is_running (THREAD_INFO_TYPE *info);