[System.Threading.Tasks.Dataflow] Replace implementation with CoreFx version
[mono.git] / mono / utils / mono-threads-state-machine.c
1 #include <config.h>
2
3 #include <mono/utils/mono-compiler.h>
4 #include <mono/utils/mono-threads.h>
5 #include <mono/utils/mono-tls.h>
6 #include <mono/utils/mono-memory-model.h>
7 #include <mono/utils/atomic.h>
8 #include <mono/utils/checked-build.h>
9
10 #include <errno.h>
11
12 /*thread state helpers*/
13 static inline int
14 get_thread_state (int thread_state)
15 {
16         return thread_state & THREAD_STATE_MASK;
17 }
18
19 static inline int
20 get_thread_suspend_count (int thread_state)
21 {
22         return (thread_state & THREAD_SUSPEND_COUNT_MASK) >> THREAD_SUSPEND_COUNT_SHIFT;
23 }
24
25 static inline int
26 build_thread_state (int thread_state, int suspend_count) 
27 {
28         g_assert (suspend_count >= 0 && suspend_count <= THREAD_SUSPEND_COUNT_MAX);
29         g_assert (thread_state >= 0 && thread_state <= STATE_MAX);
30
31         return thread_state | (suspend_count << THREAD_SUSPEND_COUNT_SHIFT);
32 }
33
34 static const char*
35 state_name (int state)
36 {
37         static const char *state_names [] = {
38                 "STARTING",
39                 "RUNNING",
40                 "DETACHED",
41                 "ASYNC_SUSPENDED",
42                 "SELF_SUSPENDED",
43                 "ASYNC_SUSPEND_REQUESTED",
44                 "SELF_SUSPEND_REQUESTED",
45                 "STATE_BLOCKING",
46                 "STATE_BLOCKING_AND_SUSPENDED",
47         };
48         return state_names [get_thread_state (state)];
49 }
50
51 #define UNWRAP_THREAD_STATE(RAW,CUR,COUNT,INFO) do {    \
52         RAW = (INFO)->thread_state;     \
53         CUR = get_thread_state (RAW);   \
54         COUNT = get_thread_suspend_count (RAW); \
55 } while (0)
56
57 static void
58 check_thread_state (MonoThreadInfo* info)
59 {
60         int raw_state, cur_state, suspend_count;
61         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
62         switch (cur_state) {
63         case STATE_STARTING:
64         case STATE_RUNNING:
65         case STATE_DETACHED:
66                 g_assert (suspend_count == 0);
67                 break;
68         case STATE_ASYNC_SUSPENDED:
69         case STATE_SELF_SUSPENDED:
70         case STATE_ASYNC_SUSPEND_REQUESTED:
71         case STATE_SELF_SUSPEND_REQUESTED:
72         case STATE_BLOCKING_AND_SUSPENDED:
73                 g_assert (suspend_count > 0);
74                 break;
75         case STATE_BLOCKING: //this is a special state that can have zero or positive suspend count.
76                 break;
77         default:
78                 g_error ("Invalid state %d", cur_state);
79         }
80 }
81
82 static inline void
83 trace_state_change (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, int suspend_count_delta)
84 {
85         check_thread_state (info);
86         THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s -> %s (%d -> %d)\n",
87                 transition,
88                 mono_thread_info_get_tid (info),
89                 state_name (get_thread_state (cur_raw_state)),
90                 state_name (next_state),
91                 get_thread_suspend_count (cur_raw_state),
92                 get_thread_suspend_count (cur_raw_state) + suspend_count_delta);
93
94         CHECKED_BUILD_THREAD_TRANSITION (transition, info, get_thread_state (cur_raw_state), get_thread_suspend_count (cur_raw_state), next_state, suspend_count_delta);
95 }
96
97 /*
98 This is the transition that signals that a thread is functioning.
99 Its main goal is to catch threads been witnessed before been fully registered.
100 */
101 void
102 mono_threads_transition_attach (MonoThreadInfo* info)
103 {
104         int raw_state, cur_state, suspend_count;
105
106 retry_state_change:
107         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
108         switch (cur_state) {
109         case STATE_STARTING:
110                 g_assert (suspend_count == 0);
111                 if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
112                         goto retry_state_change;
113                 trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, 0);
114                 break;
115         default:
116                 g_error ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
117         }
118 }
119
120 /*
121 This is the transition that signals that a thread is no longer registered with the runtime.
122 Its main goal is to catch threads been witnessed after they detach.
123
124 This returns TRUE is the transition succeeded.
125 If it returns false it means that there's a pending suspend that should be acted upon.
126 */
127 gboolean
128 mono_threads_transition_detach (MonoThreadInfo *info)
129 {
130         int raw_state, cur_state, suspend_count;
131
132 retry_state_change:
133         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
134         switch (cur_state) {
135         case STATE_RUNNING:
136                 g_assert (suspend_count == 0);
137                 if (InterlockedCompareExchange (&info->thread_state, STATE_DETACHED, raw_state) != raw_state)
138                         goto retry_state_change;
139                 trace_state_change ("DETACH", info, raw_state, STATE_DETACHED, 0);
140                 return TRUE;
141         case STATE_ASYNC_SUSPEND_REQUESTED: //Can't detach until whoever asked us to suspend to be happy with us
142                 return FALSE;
143 /*
144 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
145 STATE_SELF_SUSPENDED: Code should not be running while suspended.
146 STATE_SELF_SUSPEND_REQUESTED: This is a bug in the self suspend code that didn't execute the second part of it
147 STATE_BLOCKING: This is a bug in the coop code that forgot to do a finish blocking before exiting.
148 STATE_BLOCKING_AND_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
149 */
150         default:
151                 g_error ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
152         }
153 }
154
155 /*
156 This transition initiates the suspension of the current thread.
157 */
158 void
159 mono_threads_transition_request_self_suspension (MonoThreadInfo *info)
160 {
161         int raw_state, cur_state, suspend_count;
162         g_assert (info ==  mono_thread_info_current ());
163
164 retry_state_change:
165         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
166
167         switch (cur_state) {
168         case STATE_RUNNING: //Post a self suspend request
169                 g_assert (suspend_count == 0);
170                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
171                         goto retry_state_change;
172                 trace_state_change ("SELF_SUSPEND_REQUEST", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 1);
173                 break;
174
175         case STATE_ASYNC_SUSPEND_REQUESTED: //Bump the suspend count but don't change the request type as async takes preference
176                 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
177                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
178                         goto retry_state_change;
179                 trace_state_change ("SUSPEND_REQUEST", info, raw_state, cur_state, 1);
180                 break;
181 /*
182 Other states:
183 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
184 STATE_SELF_SUSPENDED: Code should not be running while suspended.
185 STATE_SELF_SUSPEND_REQUESTED: Self suspends should not nest as begin/end should be paired. [1]
186 STATE_BLOCKING:
187 STATE_BLOCKING_AND_SUSPENDED: Self suspension cannot be started when the thread is in blocking state as it must finish first
188
189 [1] This won't trap this sequence of requests: self suspend, async suspend and self suspend. 
190 If this turns to be an issue we can introduce a new suspend request state for when both have been requested.
191 */
192         default:
193                 g_error ("Cannot transition thread %p from %s with SUSPEND_REQUEST", mono_thread_info_get_tid (info), state_name (cur_state));
194         }
195 }
196
197 /*
198 This transition initiates the suspension of another thread.
199
200 Returns one of the following values:
201
202 - AsyncSuspendInitSuspend: Thread suspend requested, async suspend needs to be done.
203 - AsyncSuspendAlreadySuspended: Thread already suspended, nothing to do.
204 - AsyncSuspendWait: Self suspend in progress, asked it to notify us. Caller must add target to the notification set.
205 */
206 MonoRequestAsyncSuspendResult
207 mono_threads_transition_request_async_suspension (MonoThreadInfo *info)
208 {
209         int raw_state, cur_state, suspend_count;
210         g_assert (info != mono_thread_info_current ());
211
212 retry_state_change:
213         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
214
215         switch (cur_state) {
216         case STATE_RUNNING: //Post an async suspend request
217                 g_assert (suspend_count == 0);
218                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
219                         goto retry_state_change;
220                 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
221                 return AsyncSuspendInitSuspend; //This is the first async suspend request against the target
222
223         case STATE_ASYNC_SUSPENDED:
224         case STATE_SELF_SUSPENDED: //Async suspend can suspend the same thread multiple times as it starts from the outside
225         case STATE_BLOCKING_AND_SUSPENDED:
226                 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
227                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
228                         goto retry_state_change;
229                 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
230                 return AsyncSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
231
232         case STATE_SELF_SUSPEND_REQUESTED: //This suspend needs to notify the initiator, so we need to promote the suspend to async
233                 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
234                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, suspend_count + 1), raw_state) != raw_state)
235                         goto retry_state_change;
236                 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
237                 return AsyncSuspendWait; //This is the first async suspend request, change the thread and let it notify us [1]
238
239         case STATE_BLOCKING:
240                 g_assert (suspend_count < THREAD_SUSPEND_COUNT_MAX);
241                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
242                         goto retry_state_change;
243                 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
244                 return AsyncSuspendAlreadySuspended; //A thread in the blocking state has its state saved so we can treat it as suspended.
245
246 /*
247
248 [1] It's questionable on what to do if we hit the beginning of a self suspend.
249 The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
250
251 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.
252 */
253         default:
254                 g_error ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
255         }
256         return (MonoRequestAsyncSuspendResult) FALSE;
257 }
258
259 /*
260 Check the current state of the thread and try to init a self suspend.
261 This must be called with self state saved.
262
263 Returns one of the following values:
264
265 - Resumed: Async resume happened and current thread should keep running
266 - Suspend: Caller should wait for a resume signal
267 - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
268  suspend should start.
269
270 */
271 MonoSelfSupendResult
272 mono_threads_transition_state_poll (MonoThreadInfo *info)
273 {
274         int raw_state, cur_state, suspend_count;
275         g_assert (info == mono_thread_info_current ());
276
277 retry_state_change:
278         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
279         switch (cur_state) {
280         case STATE_RUNNING:
281                 g_assert (suspend_count == 0);
282                 trace_state_change ("STATE_POLL", info, raw_state, cur_state, 0);
283                 return SelfSuspendResumed; //We're fine, don't suspend
284
285         case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
286         case STATE_SELF_SUSPEND_REQUESTED: //Start the self suspend process
287                 g_assert (suspend_count > 0);
288                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
289                         goto retry_state_change;
290                 trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, 0);
291                 if (cur_state == STATE_SELF_SUSPEND_REQUESTED)
292                         return SelfSuspendWait; //Caller should wait for resume
293                 else
294                         return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
295
296 /*
297 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
298 STATE_SELF_SUSPENDED: Code should not be running while suspended.
299 STATE_BLOCKING:
300 STATE_BLOCKING_AND_SUSPENDED: Pool is a local state transition. No VM activities are allowed while in blocking mode.
301 */
302         default:
303                 g_error ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
304         }
305 }
306
307 /*
308 Try to resume a suspended thread.
309
310 Returns one of the following values:
311 - Sucess: The thread was resumed.
312 - Error: The thread was not suspended in the first place. [2]
313 - InitSelfResume: The thread is blocked on self suspend and should be resumed 
314 - InitAsycResume: The thread is blocked on async suspend and should be resumed
315 - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
316
317 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
318 used as a suspend permit and cancel each other.
319
320 Suspend permits are really useful to implement managed synchronization structures that
321 don't consume native resources. The downside is that they further complicate the design of this
322 system as the RUNNING state now has a non zero suspend counter.
323
324 It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
325
326 One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
327 This would make permits really harder to add.
328 */
329 MonoResumeResult
330 mono_threads_transition_request_resume (MonoThreadInfo* info)
331 {
332         int raw_state, cur_state, suspend_count;
333         g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
334
335 retry_state_change:
336         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
337         switch (cur_state) {
338         case STATE_RUNNING: //Thread already running.
339                 g_assert (suspend_count == 0);
340                 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
341                 return ResumeError; //Resume failed because thread was not blocked
342
343         case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
344                 if (suspend_count == 0) {
345                         trace_state_change ("RESUME", info, raw_state, cur_state, 0);
346                         return ResumeError;
347                 } else {
348                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
349                                         goto retry_state_change;
350                         trace_state_change ("RESUME", info, raw_state, cur_state, -1);
351                         return ResumeOk; //Resume worked and there's nothing for the caller to do.
352                 }
353                 break;
354         case STATE_ASYNC_SUSPENDED:
355         case STATE_SELF_SUSPENDED:
356         case STATE_BLOCKING_AND_SUSPENDED: //Decrease the suspend_count and maybe resume
357                 g_assert (suspend_count > 0);
358                 if (suspend_count > 1) {
359                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
360                                         goto retry_state_change;
361                         trace_state_change ("RESUME", info, raw_state, cur_state, -1);
362
363                         return ResumeOk; //Resume worked and there's nothing for the caller to do.
364                 } else {
365                         if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
366                                 goto retry_state_change;
367                         trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
368
369                         if (cur_state == STATE_ASYNC_SUSPENDED)
370                                 return ResumeInitAsyncResume; //Resume worked and caller must do async resume
371                         else if (cur_state == STATE_SELF_SUSPENDED)
372                                 return ResumeInitSelfResume; //Resume worked and caller must do self resume
373                         else
374                                 return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
375                 }
376
377         case STATE_SELF_SUSPEND_REQUESTED: //Self suspend was requested but another thread decided to resume it.
378                 g_assert (suspend_count > 0);
379                 if (suspend_count > 1) {
380                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
381                                         goto retry_state_change;
382                         trace_state_change ("RESUME", info, raw_state, cur_state, -1);
383                 } else {
384                         if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
385                                 goto retry_state_change;
386                         trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
387                 }
388                 return ResumeOk; //Resume worked and there's nothing for the caller to do (the target never actually suspend).
389 /*
390
391 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
392
393 [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
394 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
395
396 [4] It's questionable on whether a resume (an async operation) should be able to cancel a self suspend. The scenario where this would happen
397 is similar to the one described in [2] when this is used for as a synchronization primitive.
398
399 If this turns to be a problem we should either implement [2] or make this an invalid transition.
400
401 */
402         default:
403                 g_error ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
404         }
405 }
406
407 /*
408 This performs the last step of async suspend.
409
410 Returns TRUE if the caller should wait for resume.
411 */
412 gboolean
413 mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
414 {
415         int raw_state, cur_state, suspend_count;
416
417 retry_state_change:
418         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
419         switch (cur_state) {
420
421         case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
422         case STATE_BLOCKING_AND_SUSPENDED: //async suspend raced with blocking and lost
423                 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, 0);
424                 return FALSE; //let self suspend wait
425
426         case STATE_ASYNC_SUSPEND_REQUESTED:
427                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count), raw_state) != raw_state)
428                         goto retry_state_change;
429                 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, 0);
430                 return TRUE; //Async suspend worked, now wait for resume
431
432 /*
433 STATE_RUNNING: A thread cannot escape suspension once requested.
434 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.
435 STATE_SELF_SUSPEND_REQUESTED: When self suspend and async suspend happen together, they converge to async suspend so this state should not be visible.
436 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.
437 */
438         default:
439                 g_error ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
440         }
441 }
442
443 /*
444 This the compensatory transition for failed async suspend.
445
446 Async suspend can land on a thread as it began cleaning up and is no longer
447 functional. This happens as cleanup is a racy process from the async suspend
448 perspective. The thread could have cleaned up its domain or jit_tls, for example.
449
450 It can only transition the state as left by a sucessfull finish async suspend transition.
451
452 */
453 void
454 mono_threads_transition_async_suspend_compensation (MonoThreadInfo* info)
455 {
456         int raw_state, cur_state, suspend_count;
457
458 retry_state_change:
459         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
460         switch (cur_state) {
461
462         case STATE_ASYNC_SUSPENDED:
463                 /*
464                 Must be one since if a self suspend is in progress the thread should still be async suspendable.
465                 If count > 1 and no self suspend is in progress then it means one of the following two.
466                 - the thread was previously suspended, which means we should never reach end suspend in the first place.
467                 - another suspend happened concurrently, which means the global suspend lock didn't happen.
468                 */
469                 g_assert (suspend_count == 1);
470                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count - 1), raw_state) != raw_state)
471                         goto retry_state_change;
472                 trace_state_change ("COMPENSATE_FINISH_ASYNC_SUSPEND", info, raw_state, STATE_RUNNING, -1);
473                 break;
474 /*
475 STATE_RUNNING
476 STATE_SELF_SUSPENDED
477 STATE_ASYNC_SUSPEND_REQUESTED
478 STATE_BLOCKING
479 STATE_BLOCKING_AND_SUSPENDED
480 STATE_SELF_SUSPEND_REQUESTED: All those are invalid end states of a sucessfull finish async suspend
481 */
482         default:
483                 g_error ("Cannot transition thread %p from %s with COMPENSATE_FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
484
485         }
486 }
487
488 /*
489 This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
490
491 Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
492 That state only works as long as the only managed state touched is blitable and was pinned before the transition.
493
494 It returns the action the caller must perform:
495
496 - Continue: Entered blocking state sucessfully;
497 - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
498
499 */
500 MonoDoBlockingResult
501 mono_threads_transition_do_blocking (MonoThreadInfo* info)
502 {
503         int raw_state, cur_state, suspend_count;
504
505 retry_state_change:
506         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
507         switch (cur_state) {
508
509         case STATE_RUNNING: //transition to blocked
510                 g_assert (suspend_count == 0);
511                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count), raw_state) != raw_state)
512                         goto retry_state_change;
513                 trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, 0);
514                 return DoBlockingContinue;
515
516         case STATE_ASYNC_SUSPEND_REQUESTED:
517                 g_assert (suspend_count > 0);
518                 trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, 0);
519                 return DoBlockingPollAndRetry;
520 /*
521 STATE_ASYNC_SUSPENDED
522 STATE_SELF_SUSPENDED: Code should not be running while suspended.
523 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
524 STATE_BLOCKING:
525 STATE_BLOCKING_AND_SUSPENDED: Blocking is not nestabled
526 */
527         default:
528                 g_error ("Cannot transition thread %p from %s with DO_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
529         }
530 }
531
532 /*
533 This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
534 until its resumed before continuing.
535
536 It returns one of:
537 -Aborted: The blocking operation was aborted and not properly restored. Aborts can happen due to lazy loading and some n2m transitions;
538 -Ok: Done with blocking, just move on;
539 -Wait: This thread was async suspended, wait for resume
540
541 */
542 MonoDoneBlockingResult
543 mono_threads_transition_done_blocking (MonoThreadInfo* info)
544 {
545         int raw_state, cur_state, suspend_count;
546
547 retry_state_change:
548         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
549         switch (cur_state) {
550         case STATE_RUNNING: //Blocking was aborted and not properly restored
551         case STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
552                 trace_state_change ("DONE_BLOCKING", info, raw_state, cur_state, 0);
553                 return DoneBlockingAborted;
554
555         case STATE_BLOCKING:
556                 if (suspend_count == 0) {
557                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
558                                 goto retry_state_change;
559                         trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, 0);
560                         return DoneBlockingOk;
561                 } else {
562                         g_assert (suspend_count >= 0);
563                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING_AND_SUSPENDED, suspend_count), raw_state) != raw_state)
564                                 goto retry_state_change;
565                         trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_AND_SUSPENDED, 0);
566                         return DoneBlockingWait;
567                 }
568
569 /*
570 STATE_ASYNC_SUSPENDED
571 STATE_SELF_SUSPENDED: Code should not be running while suspended.
572 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
573 STATE_BLOCKING_AND_SUSPENDED: This an exit state of done blocking
574 */
575         default:
576                 g_error ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
577         }
578 }
579
580 /*
581 Transition a thread in what should be a blocking state back to running state.
582 This is different that done blocking because the goal is to get back to blocking once we're done.
583 This is required to be able to bail out of blocking in case we're back to inside the runtime.
584
585 It returns one of:
586 -Ignore: Thread was not in blocking, nothing to do;
587 -IgnoreAndPool: Thread was not blocking and there's a pending suspend that needs to be processed;
588 -Ok: Blocking state successfully aborted;
589 -OkAndPool: Blocking state successfully aborted, there's a pending suspend to be processed though
590 */
591 MonoAbortBlockingResult
592 mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info)
593 {
594         int raw_state, cur_state, suspend_count;
595
596 retry_state_change:
597         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
598         switch (cur_state) {
599         case STATE_RUNNING: //thread already in runnable state
600                 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
601                 return AbortBlockingIgnore;
602
603         case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
604                 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
605                 return AbortBlockingIgnoreAndPoll;
606
607         case STATE_BLOCKING:
608                 if (suspend_count == 0) {
609                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
610                                 goto retry_state_change;
611                         trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, 0);
612                         return AbortBlockingOk;
613                 } else {
614                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPEND_REQUESTED, suspend_count), raw_state) != raw_state)
615                                 goto retry_state_change;
616                         trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 0);
617                         return AbortBlockingOkAndPool;
618                 }
619 /*
620 STATE_ASYNC_SUSPENDED:
621 STATE_SELF_SUSPENDED: Code should not be running while suspended.
622 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend.
623 STATE_BLOCKING_AND_SUSPENDED: This is an exit state of done blocking, can't happen here.
624 */
625         default:
626                 g_error ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
627         }
628 }
629
630 MonoThreadUnwindState*
631 mono_thread_info_get_suspend_state (MonoThreadInfo *info)
632 {
633         int raw_state, cur_state, suspend_count;
634         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
635         switch (cur_state) {
636         case STATE_ASYNC_SUSPENDED:
637                 return &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX];
638         case STATE_SELF_SUSPENDED:
639         case STATE_BLOCKING_AND_SUSPENDED:
640                 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
641         case STATE_BLOCKING:
642                 if (suspend_count > 0)
643                         return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
644         default:
645 /*
646 STATE_RUNNING
647 STATE_SELF_SUSPENDED
648 STATE_ASYNC_SUSPEND_REQUESTED
649 STATE_BLOCKING: All those are invalid suspend states.
650 */
651                 g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info), state_name (cur_state));
652         }
653 }
654
655 // State checking code
656 /**
657  * Return TRUE is the thread is in a runnable state.
658 */
659 gboolean
660 mono_thread_info_is_running (MonoThreadInfo *info)
661 {
662         switch (get_thread_state (info->thread_state)) {
663         case STATE_RUNNING:
664         case STATE_ASYNC_SUSPEND_REQUESTED:
665         case STATE_SELF_SUSPEND_REQUESTED:
666         case STATE_BLOCKING:
667                 return TRUE;
668         }
669         return FALSE;
670 }
671
672 /**
673  * Return TRUE is the thread is in an usable (suspendable) state
674  */
675 gboolean
676 mono_thread_info_is_live (MonoThreadInfo *info)
677 {
678         switch (get_thread_state (info->thread_state)) {
679         case STATE_STARTING:
680         case STATE_DETACHED:
681                 return FALSE;
682         }
683         return TRUE;
684 }
685
686 int
687 mono_thread_info_suspend_count (MonoThreadInfo *info)
688 {
689         return get_thread_suspend_count (info->thread_state);
690 }
691
692 int
693 mono_thread_info_current_state (MonoThreadInfo *info)
694 {
695         return get_thread_state (info->thread_state);
696 }
697
698 const char*
699 mono_thread_state_name (int state)
700 {
701         return state_name (state);
702 }