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