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