[coop] Fix the state transition for a pending suspend in reset blocking.
authorRodrigo Kumpera <kumpera@gmail.com>
Tue, 1 Sep 2015 23:24:00 +0000 (19:24 -0400)
committerRodrigo Kumpera <kumpera@gmail.com>
Tue, 1 Sep 2015 23:35:02 +0000 (19:35 -0400)
The problematic scenario is the following: [1]

thread A) [DO_BLOCKING] runnable -> blocking (0)                      -- i.e, do a pinvoke
thread B) [ASYNC_SUSPEND_REQUESTED] blocking -> blocking (+1)         -- thread B tries to suspend thread A
thread A) [ABORT_BLOCKING] blocking -> async_suspend_requested (0)    -- i.e, that pinvoke called into a reverse delegate and needs to go back to runnable state
thread A) [STATE_POLL] async_suspend_requested -> self_suspended (0)  -- thread A self suspend due to the request from B

If polling witness async suspend, it must notify the initiator of a successful suspend.
If the initiator witness a thread in blocking, it bumps the suspend count, assumes it suspended and don't wait for a notification.

This means that thread A would post to the suspend semaphore but thread B (the suspend initiator) will not wait on it.

Given the initiator won't wait in this case, it makes no sense for abort blocking to put the thread in async_suspend_requested.
The solution is to put it on self_suspend_requested, which won't trigger state_poll to notify.

[1] Read it as: thread) [TRANSITION NAME] old state -> new state (suspend count delta) -- explanation

mono/utils/mono-threads-state-machine.c

index bf38fd4f24415b797d3551c9ad5036962241ccd9..098e02c7ee32e1a558cd6c103ac6a8a03e70a486 100644 (file)
@@ -611,9 +611,9 @@ 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_ASYNC_SUSPEND_REQUESTED, suspend_count), raw_state) != raw_state)
+                       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_ASYNC_SUSPEND_REQUESTED, 0);
+                       trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 0);
                        return AbortBlockingOkAndPool;
                }
 /*