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