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