Windows x64 full AOT support for mono/mini regression tests.
[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                 mono_fatal_with_history ("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                 mono_fatal_with_history ("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                 mono_fatal_with_history ("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 - AsyncSuspendBlocking: Thread in blocking state
206 */
207 MonoRequestAsyncSuspendResult
208 mono_threads_transition_request_async_suspension (MonoThreadInfo *info)
209 {
210         int raw_state, cur_state, suspend_count;
211         g_assert (info != mono_thread_info_current ());
212
213 retry_state_change:
214         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
215
216         switch (cur_state) {
217         case STATE_RUNNING: //Post an async suspend request
218                 g_assert (suspend_count == 0);
219                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
220                         goto retry_state_change;
221                 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
222                 return AsyncSuspendInitSuspend; //This is the first async suspend request against the target
223
224         case STATE_ASYNC_SUSPENDED:
225         case STATE_SELF_SUSPENDED: //Async suspend can suspend the same thread multiple times as it starts from the outside
226         case STATE_BLOCKING_AND_SUSPENDED:
227                 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
228                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
229                         goto retry_state_change;
230                 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
231                 return AsyncSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
232
233         case STATE_SELF_SUSPEND_REQUESTED: //This suspend needs to notify the initiator, so we need to promote the suspend to async
234                 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
235                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, suspend_count + 1), raw_state) != raw_state)
236                         goto retry_state_change;
237                 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
238                 return AsyncSuspendWait; //This is the first async suspend request, change the thread and let it notify us [1]
239
240         case STATE_BLOCKING:
241                 g_assert (suspend_count < THREAD_SUSPEND_COUNT_MAX);
242                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
243                         goto retry_state_change;
244                 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
245                 return AsyncSuspendBlocking; //A thread in the blocking state has its state saved so we can treat it as suspended.
246
247 /*
248
249 [1] It's questionable on what to do if we hit the beginning of a self suspend.
250 The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
251
252 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.
253 */
254         default:
255                 mono_fatal_with_history ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
256         }
257         return (MonoRequestAsyncSuspendResult) FALSE;
258 }
259
260 /*
261 Check the current state of the thread and try to init a self suspend.
262 This must be called with self state saved.
263
264 Returns one of the following values:
265
266 - Resumed: Async resume happened and current thread should keep running
267 - Suspend: Caller should wait for a resume signal
268 - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
269  suspend should start.
270
271 */
272 MonoSelfSupendResult
273 mono_threads_transition_state_poll (MonoThreadInfo *info)
274 {
275         int raw_state, cur_state, suspend_count;
276         g_assert (info == mono_thread_info_current ());
277
278 retry_state_change:
279         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
280         switch (cur_state) {
281         case STATE_RUNNING:
282                 g_assert (suspend_count == 0);
283                 trace_state_change ("STATE_POLL", info, raw_state, cur_state, 0);
284                 return SelfSuspendResumed; //We're fine, don't suspend
285
286         case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
287         case STATE_SELF_SUSPEND_REQUESTED: //Start the self suspend process
288                 g_assert (suspend_count > 0);
289                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
290                         goto retry_state_change;
291                 trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, 0);
292                 if (cur_state == STATE_SELF_SUSPEND_REQUESTED)
293                         return SelfSuspendWait; //Caller should wait for resume
294                 else
295                         return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
296
297 /*
298 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
299 STATE_SELF_SUSPENDED: Code should not be running while suspended.
300 STATE_BLOCKING:
301 STATE_BLOCKING_AND_SUSPENDED: Pool is a local state transition. No VM activities are allowed while in blocking mode.
302 */
303         default:
304                 mono_fatal_with_history ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
305         }
306 }
307
308 /*
309 Try to resume a suspended thread.
310
311 Returns one of the following values:
312 - Sucess: The thread was resumed.
313 - Error: The thread was not suspended in the first place. [2]
314 - InitSelfResume: The thread is blocked on self suspend and should be resumed 
315 - InitAsycResume: The thread is blocked on async suspend and should be resumed
316 - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
317
318 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
319 used as a suspend permit and cancel each other.
320
321 Suspend permits are really useful to implement managed synchronization structures that
322 don't consume native resources. The downside is that they further complicate the design of this
323 system as the RUNNING state now has a non zero suspend counter.
324
325 It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
326
327 One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
328 This would make permits really harder to add.
329 */
330 MonoResumeResult
331 mono_threads_transition_request_resume (MonoThreadInfo* info)
332 {
333         int raw_state, cur_state, suspend_count;
334         g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
335
336 retry_state_change:
337         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
338         switch (cur_state) {
339         case STATE_RUNNING: //Thread already running.
340                 g_assert (suspend_count == 0);
341                 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
342                 return ResumeError; //Resume failed because thread was not blocked
343
344         case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
345                 if (suspend_count == 0) {
346                         trace_state_change ("RESUME", info, raw_state, cur_state, 0);
347                         return ResumeError;
348                 } else {
349                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
350                                         goto retry_state_change;
351                         trace_state_change ("RESUME", info, raw_state, cur_state, -1);
352                         return ResumeOk; //Resume worked and there's nothing for the caller to do.
353                 }
354                 break;
355         case STATE_ASYNC_SUSPENDED:
356         case STATE_SELF_SUSPENDED:
357         case STATE_BLOCKING_AND_SUSPENDED: //Decrease the suspend_count and maybe resume
358                 g_assert (suspend_count > 0);
359                 if (suspend_count > 1) {
360                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
361                                         goto retry_state_change;
362                         trace_state_change ("RESUME", info, raw_state, cur_state, -1);
363
364                         return ResumeOk; //Resume worked and there's nothing for the caller to do.
365                 } else {
366                         if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
367                                 goto retry_state_change;
368                         trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
369
370                         if (cur_state == STATE_ASYNC_SUSPENDED)
371                                 return ResumeInitAsyncResume; //Resume worked and caller must do async resume
372                         else if (cur_state == STATE_SELF_SUSPENDED)
373                                 return ResumeInitSelfResume; //Resume worked and caller must do self resume
374                         else
375                                 return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
376                 }
377
378         case STATE_SELF_SUSPEND_REQUESTED: //Self suspend was requested but another thread decided to resume it.
379                 g_assert (suspend_count > 0);
380                 if (suspend_count > 1) {
381                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
382                                         goto retry_state_change;
383                         trace_state_change ("RESUME", info, raw_state, cur_state, -1);
384                 } else {
385                         if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
386                                 goto retry_state_change;
387                         trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
388                 }
389                 return ResumeOk; //Resume worked and there's nothing for the caller to do (the target never actually suspend).
390 /*
391
392 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
393
394 [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
395 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
396
397 [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
398 is similar to the one described in [2] when this is used for as a synchronization primitive.
399
400 If this turns to be a problem we should either implement [2] or make this an invalid transition.
401
402 */
403         default:
404                 mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
405         }
406 }
407
408 /*
409 This performs the last step of async suspend.
410
411 Returns TRUE if the caller should wait for resume.
412 */
413 gboolean
414 mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
415 {
416         int raw_state, cur_state, suspend_count;
417
418 retry_state_change:
419         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
420         switch (cur_state) {
421
422         case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
423         case STATE_BLOCKING_AND_SUSPENDED: //async suspend raced with blocking and lost
424                 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, 0);
425                 return FALSE; //let self suspend wait
426
427         case STATE_ASYNC_SUSPEND_REQUESTED:
428                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count), raw_state) != raw_state)
429                         goto retry_state_change;
430                 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, 0);
431                 return TRUE; //Async suspend worked, now wait for resume
432
433 /*
434 STATE_RUNNING: A thread cannot escape suspension once requested.
435 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.
436 STATE_SELF_SUSPEND_REQUESTED: When self suspend and async suspend happen together, they converge to async suspend so this state should not be visible.
437 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.
438 */
439         default:
440                 mono_fatal_with_history ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
441         }
442 }
443
444 /*
445 This the compensatory transition for failed async suspend.
446
447 Async suspend can land on a thread as it began cleaning up and is no longer
448 functional. This happens as cleanup is a racy process from the async suspend
449 perspective. The thread could have cleaned up its domain or jit_tls, for example.
450
451 It can only transition the state as left by a sucessfull finish async suspend transition.
452
453 */
454 void
455 mono_threads_transition_async_suspend_compensation (MonoThreadInfo* info)
456 {
457         int raw_state, cur_state, suspend_count;
458
459 retry_state_change:
460         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
461         switch (cur_state) {
462
463         case STATE_ASYNC_SUSPENDED:
464                 /*
465                 Must be one since if a self suspend is in progress the thread should still be async suspendable.
466                 If count > 1 and no self suspend is in progress then it means one of the following two.
467                 - the thread was previously suspended, which means we should never reach end suspend in the first place.
468                 - another suspend happened concurrently, which means the global suspend lock didn't happen.
469                 */
470                 g_assert (suspend_count == 1);
471                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count - 1), raw_state) != raw_state)
472                         goto retry_state_change;
473                 trace_state_change ("COMPENSATE_FINISH_ASYNC_SUSPEND", info, raw_state, STATE_RUNNING, -1);
474                 break;
475 /*
476 STATE_RUNNING
477 STATE_SELF_SUSPENDED
478 STATE_ASYNC_SUSPEND_REQUESTED
479 STATE_BLOCKING
480 STATE_BLOCKING_AND_SUSPENDED
481 STATE_SELF_SUSPEND_REQUESTED: All those are invalid end states of a sucessfull finish async suspend
482 */
483         default:
484                 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));
485
486         }
487 }
488
489 /*
490 This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
491
492 Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
493 That state only works as long as the only managed state touched is blitable and was pinned before the transition.
494
495 It returns the action the caller must perform:
496
497 - Continue: Entered blocking state sucessfully;
498 - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
499
500 */
501 MonoDoBlockingResult
502 mono_threads_transition_do_blocking (MonoThreadInfo* info)
503 {
504         int raw_state, cur_state, suspend_count;
505
506 retry_state_change:
507         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
508         switch (cur_state) {
509
510         case STATE_RUNNING: //transition to blocked
511                 g_assert (suspend_count == 0);
512                 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count), raw_state) != raw_state)
513                         goto retry_state_change;
514                 trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, 0);
515                 return DoBlockingContinue;
516
517         case STATE_ASYNC_SUSPEND_REQUESTED:
518                 g_assert (suspend_count > 0);
519                 trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, 0);
520                 return DoBlockingPollAndRetry;
521 /*
522 STATE_ASYNC_SUSPENDED
523 STATE_SELF_SUSPENDED: Code should not be running while suspended.
524 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
525 STATE_BLOCKING:
526 STATE_BLOCKING_AND_SUSPENDED: Blocking is not nestabled
527 */
528         default:
529                 mono_fatal_with_history ("Cannot transition thread %p from %s with DO_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
530         }
531 }
532
533 /*
534 This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
535 until its resumed before continuing.
536
537 It returns one of:
538 -Aborted: The blocking operation was aborted and not properly restored. Aborts can happen due to lazy loading and some n2m transitions;
539 -Ok: Done with blocking, just move on;
540 -Wait: This thread was async suspended, wait for resume
541
542 */
543 MonoDoneBlockingResult
544 mono_threads_transition_done_blocking (MonoThreadInfo* info)
545 {
546         int raw_state, cur_state, suspend_count;
547
548 retry_state_change:
549         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
550         switch (cur_state) {
551         case STATE_RUNNING: //Blocking was aborted and not properly restored
552         case STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
553                 trace_state_change ("DONE_BLOCKING", info, raw_state, cur_state, 0);
554                 return DoneBlockingAborted;
555
556         case STATE_BLOCKING:
557                 if (suspend_count == 0) {
558                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
559                                 goto retry_state_change;
560                         trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, 0);
561                         return DoneBlockingOk;
562                 } else {
563                         g_assert (suspend_count >= 0);
564                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING_AND_SUSPENDED, suspend_count), raw_state) != raw_state)
565                                 goto retry_state_change;
566                         trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_AND_SUSPENDED, 0);
567                         return DoneBlockingWait;
568                 }
569
570 /*
571 STATE_ASYNC_SUSPENDED
572 STATE_SELF_SUSPENDED: Code should not be running while suspended.
573 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
574 STATE_BLOCKING_AND_SUSPENDED: This an exit state of done blocking
575 */
576         default:
577                 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
578         }
579 }
580
581 /*
582 Transition a thread in what should be a blocking state back to running state.
583 This is different that done blocking because the goal is to get back to blocking once we're done.
584 This is required to be able to bail out of blocking in case we're back to inside the runtime.
585
586 It returns one of:
587 -Ignore: Thread was not in blocking, nothing to do;
588 -IgnoreAndPool: Thread was not blocking and there's a pending suspend that needs to be processed;
589 -Ok: Blocking state successfully aborted;
590 -OkAndPool: Blocking state successfully aborted, there's a pending suspend to be processed though
591 */
592 MonoAbortBlockingResult
593 mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info)
594 {
595         int raw_state, cur_state, suspend_count;
596
597 retry_state_change:
598         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
599         switch (cur_state) {
600         case STATE_RUNNING: //thread already in runnable state
601                 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
602                 return AbortBlockingIgnore;
603
604         case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
605                 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
606                 return AbortBlockingIgnoreAndPoll;
607
608         case STATE_BLOCKING:
609                 if (suspend_count == 0) {
610                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
611                                 goto retry_state_change;
612                         trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, 0);
613                         return AbortBlockingOk;
614                 } else {
615                         if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPEND_REQUESTED, suspend_count), raw_state) != raw_state)
616                                 goto retry_state_change;
617                         trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 0);
618                         return AbortBlockingOkAndPool;
619                 }
620 /*
621 STATE_ASYNC_SUSPENDED:
622 STATE_SELF_SUSPENDED: Code should not be running while suspended.
623 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend.
624 STATE_BLOCKING_AND_SUSPENDED: This is an exit state of done blocking, can't happen here.
625 */
626         default:
627                 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
628         }
629 }
630
631 MonoThreadUnwindState*
632 mono_thread_info_get_suspend_state (MonoThreadInfo *info)
633 {
634         int raw_state, cur_state, suspend_count;
635         UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
636         switch (cur_state) {
637         case STATE_ASYNC_SUSPENDED:
638                 return &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX];
639         case STATE_SELF_SUSPENDED:
640         case STATE_BLOCKING_AND_SUSPENDED:
641                 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
642         case STATE_BLOCKING:
643                 if (suspend_count > 0)
644                         return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
645         default:
646 /*
647 STATE_RUNNING
648 STATE_SELF_SUSPENDED
649 STATE_ASYNC_SUSPEND_REQUESTED
650 STATE_BLOCKING: All those are invalid suspend states.
651 */
652                 g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info), state_name (cur_state));
653         }
654 }
655
656 // State checking code
657 /**
658  * Return TRUE is the thread is in a runnable state.
659 */
660 gboolean
661 mono_thread_info_is_running (MonoThreadInfo *info)
662 {
663         switch (get_thread_state (info->thread_state)) {
664         case STATE_RUNNING:
665         case STATE_ASYNC_SUSPEND_REQUESTED:
666         case STATE_SELF_SUSPEND_REQUESTED:
667         case STATE_BLOCKING:
668                 return TRUE;
669         }
670         return FALSE;
671 }
672
673 /**
674  * Return TRUE is the thread is in an usable (suspendable) state
675  */
676 gboolean
677 mono_thread_info_is_live (MonoThreadInfo *info)
678 {
679         switch (get_thread_state (info->thread_state)) {
680         case STATE_STARTING:
681         case STATE_DETACHED:
682                 return FALSE;
683         }
684         return TRUE;
685 }
686
687 int
688 mono_thread_info_suspend_count (MonoThreadInfo *info)
689 {
690         return get_thread_suspend_count (info->thread_state);
691 }
692
693 int
694 mono_thread_info_current_state (MonoThreadInfo *info)
695 {
696         return get_thread_state (info->thread_state);
697 }
698
699 const char*
700 mono_thread_state_name (int state)
701 {
702         return state_name (state);
703 }