[sgen] Evacuate from emptier blocks to fuller ones
[mono.git] / mono / utils / mono-threads-state-machine.c
index 9bcaf4fd9b55e6fbe607eced9fbe6e43c3eb458c..32491a4ca225229e622799504f8d7cc6012c031c 100644 (file)
@@ -5,6 +5,7 @@
 #include <mono/utils/mono-tls.h>
 #include <mono/utils/mono-memory-model.h>
 #include <mono/utils/atomic.h>
+#include <mono/utils/checked-build.h>
 
 #include <errno.h>
 
@@ -41,8 +42,8 @@ state_name (int state)
                "SELF_SUSPENDED",
                "ASYNC_SUSPEND_REQUESTED",
                "SELF_SUSPEND_REQUESTED",
-               "SUSPEND_IN_PROGRESS",
-               "SUSPEND_PROMOTED_TO_ASYNC",
+               "STATE_BLOCKING",
+               "STATE_BLOCKING_AND_SUSPENDED",
        };
        return state_names [get_thread_state (state)];
 }
@@ -68,10 +69,11 @@ check_thread_state (MonoThreadInfo* info)
        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:
+       case STATE_BLOCKING_AND_SUSPENDED:
                g_assert (suspend_count > 0);
                break;
+       case STATE_BLOCKING: //this is a special state that can have zero or positive suspend count.
+               break;
        default:
                g_error ("Invalid state %d", cur_state);
        }
@@ -88,6 +90,8 @@ trace_state_change (const char *transition, MonoThreadInfo *info, int cur_raw_st
                state_name (next_state),
                get_thread_suspend_count (cur_raw_state),
                get_thread_suspend_count (cur_raw_state) + suspend_count_delta);
+
+       CHECKED_BUILD_THREAD_TRANSITION (transition, info, get_thread_state (cur_raw_state), get_thread_suspend_count (cur_raw_state), next_state, suspend_count_delta);
 }
 
 /*
@@ -109,7 +113,7 @@ retry_state_change:
                trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, 0);
                break;
        default:
-               g_error ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
+               mono_fatal_with_history ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
        }
 }
 
@@ -140,11 +144,11 @@ retry_state_change:
 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
+STATE_BLOCKING: This is a bug in the coop code that forgot to do a finish blocking before exiting.
+STATE_BLOCKING_AND_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
 */
        default:
-               g_error ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
+               mono_fatal_with_history ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
        }
 }
 
@@ -179,23 +183,28 @@ Other states:
 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
+STATE_BLOCKING:
+STATE_BLOCKING_AND_SUSPENDED: Self suspension cannot be started when the thread is in blocking state as it must finish first
 
 [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.
 */
        default:
-               g_error ("Cannot transition thread %p from %s with SUSPEND_REQUEST", info, state_name (cur_state));
+               mono_fatal_with_history ("Cannot transition thread %p from %s with SUSPEND_REQUEST", mono_thread_info_get_tid (info), state_name (cur_state));
        }
 }
 
 /*
 This transition initiates the suspension of another thread.
 
-Returns TRUE if suspension notification will happen and FALSE if the target is already suspended.
+Returns one of the following values:
+
+- AsyncSuspendInitSuspend: Thread suspend requested, async suspend needs to be done.
+- AsyncSuspendAlreadySuspended: Thread already suspended, nothing to do.
+- AsyncSuspendWait: Self suspend in progress, asked it to notify us. Caller must add target to the notification set.
+- AsyncSuspendBlocking: Thread in blocking state
 */
-gboolean
+MonoRequestAsyncSuspendResult
 mono_threads_transition_request_async_suspension (MonoThreadInfo *info)
 {
        int raw_state, cur_state, suspend_count;
@@ -210,48 +219,57 @@ retry_state_change:
                if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
                        goto retry_state_change;
                trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
-               return TRUE; //This is the first async suspend request against the target
+               return AsyncSuspendInitSuspend; //This is the first async suspend request against the target
 
        case STATE_ASYNC_SUSPENDED:
        case STATE_SELF_SUSPENDED: //Async suspend can suspend the same thread multiple times as it starts from the outside
+       case STATE_BLOCKING_AND_SUSPENDED:
                g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
                if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
                        goto retry_state_change;
                trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
-               return FALSE; //Thread is already suspended so we don't need to wait it to suspend
+               return AsyncSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
 
        case STATE_SELF_SUSPEND_REQUESTED: //This suspend needs to notify the initiator, so we need to promote the suspend to async
                g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
                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 TRUE; //This is the first async suspend request against the target
+               return AsyncSuspendWait; //This is the first async suspend request, change the thread and let it notify us [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)
+       case STATE_BLOCKING:
+               g_assert (suspend_count < THREAD_SUSPEND_COUNT_MAX);
+               if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, 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 TRUE; //This is a self suspend in progress that now needs to notify the initiator
-               break;
+               trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
+               return AsyncSuspendBlocking; //A thread in the blocking state has its state saved so we can treat it as suspended.
+
 /*
+
+[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.
+
 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));
+               mono_fatal_with_history ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
        }
-       return FALSE;
+       return (MonoRequestAsyncSuspendResult) FALSE;
 }
 
 /*
 Check the current state of the thread and try to init a self suspend.
+This must be called with self state saved.
 
-Returns TRUE is self suspend should start.
+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
+ suspend should start.
 
 */
-gboolean
+MonoSelfSupendResult
 mono_threads_transition_state_poll (MonoThreadInfo *info)
 {
        int raw_state, cur_state, suspend_count;
@@ -263,30 +281,27 @@ retry_state_change:
        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
+STATE_BLOCKING:
+STATE_BLOCKING_AND_SUSPENDED: Pool is a local state transition. No VM activities are allowed while in blocking mode.
 */
        default:
-               g_error ("Cannot transition thread %p from %s with STATE_POLL", info, state_name (cur_state));
+               mono_fatal_with_history ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
        }
 }
 
@@ -298,6 +313,7 @@ Returns one of the following values:
 - Error: The thread was not suspended in the first place. [2]
 - InitSelfResume: The thread is blocked on self suspend and should be resumed 
 - InitAsycResume: The thread is blocked on async suspend and should be resumed
+- ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
 
 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
 used as a suspend permit and cancel each other.
@@ -321,11 +337,24 @@ retry_state_change:
        UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
        switch (cur_state) {
        case STATE_RUNNING: //Thread already running.
+               g_assert (suspend_count == 0);
                trace_state_change ("RESUME", info, raw_state, cur_state, 0);
                return ResumeError; //Resume failed because thread was not blocked
 
+       case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
+               if (suspend_count == 0) {
+                       trace_state_change ("RESUME", info, raw_state, cur_state, 0);
+                       return ResumeError;
+               } else {
+                       if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
+                                       goto retry_state_change;
+                       trace_state_change ("RESUME", info, raw_state, cur_state, -1);
+                       return ResumeOk; //Resume worked and there's nothing for the caller to do.
+               }
+               break;
        case STATE_ASYNC_SUSPENDED:
-       case STATE_SELF_SUSPENDED: //Decrease the suspend_count and maybe resume
+       case STATE_SELF_SUSPENDED:
+       case STATE_BLOCKING_AND_SUSPENDED: //Decrease the suspend_count and maybe resume
                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)
@@ -340,12 +369,13 @@ retry_state_change:
 
                        if (cur_state == STATE_ASYNC_SUSPENDED)
                                return ResumeInitAsyncResume; //Resume worked and caller must do async resume
-                       else
+                       else if (cur_state == STATE_SELF_SUSPENDED)
                                return ResumeInitSelfResume; //Resume worked and caller must do self resume
+                       else
+                               return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
                }
 
        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]
                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)
@@ -357,11 +387,9 @@ retry_state_change:
                        trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
                }
                return ResumeOk; //Resume worked and there's nothing for the caller to do (the target never actually suspend).
-
 /*
 
 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
-STATE_SUSPEND_PROMOTED_TO_ASYNC: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
 
 [3] A self-resume makes no sense given it requires the thread to be running, which means its suspend count must be zero. A self resume would make
 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
@@ -373,53 +401,7 @@ If this turns to be a problem we should either implement [2] or make this an inv
 
 */
        default:
-               g_error ("Cannot transition thread %p from %s with REQUEST_RESUME", info, state_name (cur_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));
-
+               mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
        }
 }
 
@@ -438,6 +420,7 @@ retry_state_change:
        switch (cur_state) {
 
        case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
+       case STATE_BLOCKING_AND_SUSPENDED: //async suspend raced with blocking and lost
                trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, 0);
                return FALSE; //let self suspend wait
 
@@ -447,20 +430,14 @@ 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
 /*
 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.
 STATE_SELF_SUSPEND_REQUESTED: When self suspend and async suspend happen together, they converge to async suspend so this state should not be visible.
-STATE_SUSPEND_PROMOTED_TO_ASYNC: Given there's a single initiator this cannot happen.
+STATE_BLOCKING: Async suspend only begins if a transition to async suspend requested happened. Blocking would have put us into blocking with positive suspend count if it raced with async finish.
 */
        default:
-               g_error ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", info, state_name (cur_state));
-
+               mono_fatal_with_history ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
        }
 }
 
@@ -499,18 +476,182 @@ retry_state_change:
 STATE_RUNNING
 STATE_SELF_SUSPENDED
 STATE_ASYNC_SUSPEND_REQUESTED
-STATE_SELF_SUSPEND_REQUESTED
-STATE_SUSPEND_PROMOTED_TO_ASYNC:
-STATE_SUSPEND_IN_PROGRESS: All those are invalid end states of a sucessfull finish async suspend
+STATE_BLOCKING
+STATE_BLOCKING_AND_SUSPENDED
+STATE_SELF_SUSPEND_REQUESTED: All those are invalid end states of a sucessfull finish async suspend
 */
        default:
-               g_error ("Cannot transition thread %p from %s with COMPENSATE_FINISH_ASYNC_SUSPEND", info, state_name (cur_state));
+               mono_fatal_with_history ("Cannot transition thread %p from %s with COMPENSATE_FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
 
        }
 }
 
+/*
+This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
+
+Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
+That state only works as long as the only managed state touched is blitable and was pinned before the transition.
 
+It returns the action the caller must perform:
 
+- Continue: Entered blocking state sucessfully;
+- PollAndRetry: Async suspend raced and won, try to suspend and then retry;
+
+*/
+MonoDoBlockingResult
+mono_threads_transition_do_blocking (MonoThreadInfo* info)
+{
+       int raw_state, cur_state, suspend_count;
+
+retry_state_change:
+       UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
+       switch (cur_state) {
+
+       case STATE_RUNNING: //transition to blocked
+               g_assert (suspend_count == 0);
+               if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count), raw_state) != raw_state)
+                       goto retry_state_change;
+               trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, 0);
+               return DoBlockingContinue;
+
+       case STATE_ASYNC_SUSPEND_REQUESTED:
+               g_assert (suspend_count > 0);
+               trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, 0);
+               return DoBlockingPollAndRetry;
+/*
+STATE_ASYNC_SUSPENDED
+STATE_SELF_SUSPENDED: Code should not be running while suspended.
+STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
+STATE_BLOCKING:
+STATE_BLOCKING_AND_SUSPENDED: Blocking is not nestabled
+*/
+       default:
+               mono_fatal_with_history ("Cannot transition thread %p from %s with DO_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
+       }
+}
+
+/*
+This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
+until its resumed before continuing.
+
+It returns one of:
+-Aborted: The blocking operation was aborted and not properly restored. Aborts can happen due to lazy loading and some n2m transitions;
+-Ok: Done with blocking, just move on;
+-Wait: This thread was async suspended, wait for resume
+
+*/
+MonoDoneBlockingResult
+mono_threads_transition_done_blocking (MonoThreadInfo* info)
+{
+       int raw_state, cur_state, suspend_count;
+
+retry_state_change:
+       UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
+       switch (cur_state) {
+       case STATE_RUNNING: //Blocking was aborted and not properly restored
+       case STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
+               trace_state_change ("DONE_BLOCKING", info, raw_state, cur_state, 0);
+               return DoneBlockingAborted;
+
+       case STATE_BLOCKING:
+               if (suspend_count == 0) {
+                       if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
+                               goto retry_state_change;
+                       trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, 0);
+                       return DoneBlockingOk;
+               } else {
+                       g_assert (suspend_count >= 0);
+                       if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING_AND_SUSPENDED, suspend_count), raw_state) != raw_state)
+                               goto retry_state_change;
+                       trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_AND_SUSPENDED, 0);
+                       return DoneBlockingWait;
+               }
+
+/*
+STATE_ASYNC_SUSPENDED
+STATE_SELF_SUSPENDED: Code should not be running while suspended.
+STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
+STATE_BLOCKING_AND_SUSPENDED: This an exit state of done blocking
+*/
+       default:
+               mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
+       }
+}
+
+/*
+Transition a thread in what should be a blocking state back to running state.
+This is different that done blocking because the goal is to get back to blocking once we're done.
+This is required to be able to bail out of blocking in case we're back to inside the runtime.
+
+It returns one of:
+-Ignore: Thread was not in blocking, nothing to do;
+-IgnoreAndPool: Thread was not blocking and there's a pending suspend that needs to be processed;
+-Ok: Blocking state successfully aborted;
+-OkAndPool: Blocking state successfully aborted, there's a pending suspend to be processed though
+*/
+MonoAbortBlockingResult
+mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info)
+{
+       int raw_state, cur_state, suspend_count;
+
+retry_state_change:
+       UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
+       switch (cur_state) {
+       case STATE_RUNNING: //thread already in runnable state
+               trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
+               return AbortBlockingIgnore;
+
+       case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
+               trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
+               return AbortBlockingIgnoreAndPoll;
+
+       case STATE_BLOCKING:
+               if (suspend_count == 0) {
+                       if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
+                               goto retry_state_change;
+                       trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, 0);
+                       return AbortBlockingOk;
+               } else {
+                       if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPEND_REQUESTED, suspend_count), raw_state) != raw_state)
+                               goto retry_state_change;
+                       trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 0);
+                       return AbortBlockingOkAndPool;
+               }
+/*
+STATE_ASYNC_SUSPENDED:
+STATE_SELF_SUSPENDED: Code should not be running while suspended.
+STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend.
+STATE_BLOCKING_AND_SUSPENDED: This is an exit state of done blocking, can't happen here.
+*/
+       default:
+               mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
+       }
+}
+
+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:
+       case STATE_BLOCKING_AND_SUSPENDED:
+               return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
+       case STATE_BLOCKING:
+               if (suspend_count > 0)
+                       return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
+       default:
+/*
+STATE_RUNNING
+STATE_SELF_SUSPENDED
+STATE_ASYNC_SUSPEND_REQUESTED
+STATE_BLOCKING: All those are invalid suspend states.
+*/
+               g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info), state_name (cur_state));
+       }
+}
 
 // State checking code
 /**
@@ -523,6 +664,7 @@ mono_thread_info_is_running (MonoThreadInfo *info)
        case STATE_RUNNING:
        case STATE_ASYNC_SUSPEND_REQUESTED:
        case STATE_SELF_SUSPEND_REQUESTED:
+       case STATE_BLOCKING:
                return TRUE;
        }
        return FALSE;
@@ -546,4 +688,16 @@ int
 mono_thread_info_suspend_count (MonoThreadInfo *info)
 {
        return get_thread_suspend_count (info->thread_state);
-}
\ No newline at end of file
+}
+
+int
+mono_thread_info_current_state (MonoThreadInfo *info)
+{
+       return get_thread_state (info->thread_state);
+}
+
+const char*
+mono_thread_state_name (int state)
+{
+       return state_name (state);
+}