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>
12 /*thread state helpers*/
14 get_thread_state (int thread_state)
16 return thread_state & THREAD_STATE_MASK;
20 get_thread_suspend_count (int thread_state)
22 return (thread_state & THREAD_SUSPEND_COUNT_MASK) >> THREAD_SUSPEND_COUNT_SHIFT;
26 build_thread_state (int thread_state, int suspend_count)
28 g_assert (suspend_count >= 0 && suspend_count <= THREAD_SUSPEND_COUNT_MAX);
29 g_assert (thread_state >= 0 && thread_state <= STATE_MAX);
31 return thread_state | (suspend_count << THREAD_SUSPEND_COUNT_SHIFT);
35 state_name (int state)
37 static const char *state_names [] = {
43 "ASYNC_SUSPEND_REQUESTED",
44 "SELF_SUSPEND_REQUESTED",
46 "STATE_BLOCKING_AND_SUSPENDED",
48 return state_names [get_thread_state (state)];
51 #define UNWRAP_THREAD_STATE(RAW,CUR,COUNT,INFO) do { \
52 RAW = (INFO)->thread_state; \
53 CUR = get_thread_state (RAW); \
54 COUNT = get_thread_suspend_count (RAW); \
58 check_thread_state (MonoThreadInfo* info)
60 int raw_state, cur_state, suspend_count;
61 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
66 g_assert (suspend_count == 0);
68 case STATE_ASYNC_SUSPENDED:
69 case STATE_SELF_SUSPENDED:
70 case STATE_ASYNC_SUSPEND_REQUESTED:
71 case STATE_SELF_SUSPEND_REQUESTED:
72 case STATE_BLOCKING_AND_SUSPENDED:
73 g_assert (suspend_count > 0);
75 case STATE_BLOCKING: //this is a special state that can have zero or positive suspend count.
78 g_error ("Invalid state %d", cur_state);
83 trace_state_change (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, int suspend_count_delta)
85 check_thread_state (info);
86 THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s -> %s (%d -> %d)\n",
88 mono_thread_info_get_tid (info),
89 state_name (get_thread_state (cur_raw_state)),
90 state_name (next_state),
91 get_thread_suspend_count (cur_raw_state),
92 get_thread_suspend_count (cur_raw_state) + suspend_count_delta);
94 CHECKED_BUILD_THREAD_TRANSITION (transition, info, get_thread_state (cur_raw_state), get_thread_suspend_count (cur_raw_state), next_state, suspend_count_delta);
98 This is the transition that signals that a thread is functioning.
99 Its main goal is to catch threads been witnessed before been fully registered.
102 mono_threads_transition_attach (MonoThreadInfo* info)
104 int raw_state, cur_state, suspend_count;
107 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
110 if (!(suspend_count == 0))
111 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
112 if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
113 goto retry_state_change;
114 trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, 0);
117 mono_fatal_with_history ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
122 This is the transition that signals that a thread is no longer registered with the runtime.
123 Its main goal is to catch threads been witnessed after they detach.
125 This returns TRUE is the transition succeeded.
126 If it returns false it means that there's a pending suspend that should be acted upon.
129 mono_threads_transition_detach (MonoThreadInfo *info)
131 int raw_state, cur_state, suspend_count;
134 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
137 case STATE_BLOCKING: /* An OS thread on coop goes STARTING->BLOCKING->RUNNING->BLOCKING->DETACHED */
138 if (!(suspend_count == 0))
139 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
140 if (InterlockedCompareExchange (&info->thread_state, STATE_DETACHED, raw_state) != raw_state)
141 goto retry_state_change;
142 trace_state_change ("DETACH", info, raw_state, STATE_DETACHED, 0);
144 case STATE_ASYNC_SUSPEND_REQUESTED: //Can't detach until whoever asked us to suspend to be happy with us
148 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
149 STATE_SELF_SUSPENDED: Code should not be running while suspended.
150 STATE_SELF_SUSPEND_REQUESTED: This is a bug in the self suspend code that didn't execute the second part of it
151 STATE_BLOCKING_AND_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
154 mono_fatal_with_history ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
159 This transition initiates the suspension of the current thread.
162 mono_threads_transition_request_self_suspension (MonoThreadInfo *info)
164 int raw_state, cur_state, suspend_count;
165 g_assert (info == mono_thread_info_current ());
168 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
171 case STATE_RUNNING: //Post a self suspend request
172 if (!(suspend_count == 0))
173 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
174 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
175 goto retry_state_change;
176 trace_state_change ("SELF_SUSPEND_REQUEST", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 1);
179 case STATE_ASYNC_SUSPEND_REQUESTED: //Bump the suspend count but don't change the request type as async takes preference
180 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
181 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
182 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
183 goto retry_state_change;
184 trace_state_change ("SUSPEND_REQUEST", info, raw_state, cur_state, 1);
188 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
189 STATE_SELF_SUSPENDED: Code should not be running while suspended.
190 STATE_SELF_SUSPEND_REQUESTED: Self suspends should not nest as begin/end should be paired. [1]
192 STATE_BLOCKING_AND_SUSPENDED: Self suspension cannot be started when the thread is in blocking state as it must finish first
194 [1] This won't trap this sequence of requests: self suspend, async suspend and self suspend.
195 If this turns to be an issue we can introduce a new suspend request state for when both have been requested.
198 mono_fatal_with_history ("Cannot transition thread %p from %s with SUSPEND_REQUEST", mono_thread_info_get_tid (info), state_name (cur_state));
203 This transition initiates the suspension of another thread.
205 Returns one of the following values:
207 - AsyncSuspendInitSuspend: Thread suspend requested, async suspend needs to be done.
208 - AsyncSuspendAlreadySuspended: Thread already suspended, nothing to do.
209 - AsyncSuspendWait: Self suspend in progress, asked it to notify us. Caller must add target to the notification set.
210 - AsyncSuspendBlocking: Thread in blocking state
212 MonoRequestAsyncSuspendResult
213 mono_threads_transition_request_async_suspension (MonoThreadInfo *info)
215 int raw_state, cur_state, suspend_count;
216 g_assert (info != mono_thread_info_current ());
219 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
222 case STATE_RUNNING: //Post an async suspend request
223 if (!(suspend_count == 0))
224 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
225 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
226 goto retry_state_change;
227 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
228 return AsyncSuspendInitSuspend; //This is the first async suspend request against the target
230 case STATE_ASYNC_SUSPENDED:
231 case STATE_SELF_SUSPENDED: //Async suspend can suspend the same thread multiple times as it starts from the outside
232 case STATE_BLOCKING_AND_SUSPENDED:
233 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
234 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
235 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
236 goto retry_state_change;
237 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
238 return AsyncSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
240 case STATE_SELF_SUSPEND_REQUESTED: //This suspend needs to notify the initiator, so we need to promote the suspend to async
241 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
242 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
243 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, suspend_count + 1), raw_state) != raw_state)
244 goto retry_state_change;
245 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
246 return AsyncSuspendWait; //This is the first async suspend request, change the thread and let it notify us [1]
249 if (!(suspend_count < THREAD_SUSPEND_COUNT_MAX))
250 mono_fatal_with_history ("suspend_count = %d, but should be < THREAD_SUSPEND_COUNT_MAX", suspend_count);
251 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
252 goto retry_state_change;
253 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
254 return AsyncSuspendBlocking; //A thread in the blocking state has its state saved so we can treat it as suspended.
258 [1] It's questionable on what to do if we hit the beginning of a self suspend.
259 The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
261 STATE_ASYNC_SUSPEND_REQUESTED: Since there can only be one async suspend in progress and it must finish, it should not be possible to witness this.
264 mono_fatal_with_history ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
266 return (MonoRequestAsyncSuspendResult) FALSE;
270 Check the current state of the thread and try to init a self suspend.
271 This must be called with self state saved.
273 Returns one of the following values:
275 - Resumed: Async resume happened and current thread should keep running
276 - Suspend: Caller should wait for a resume signal
277 - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
278 suspend should start.
282 mono_threads_transition_state_poll (MonoThreadInfo *info)
284 int raw_state, cur_state, suspend_count;
285 g_assert (info == mono_thread_info_current ());
288 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
291 if (!(suspend_count == 0))
292 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
293 trace_state_change ("STATE_POLL", info, raw_state, cur_state, 0);
294 return SelfSuspendResumed; //We're fine, don't suspend
296 case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
297 case STATE_SELF_SUSPEND_REQUESTED: //Start the self suspend process
298 if (!(suspend_count > 0))
299 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
300 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
301 goto retry_state_change;
302 trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, 0);
303 if (cur_state == STATE_SELF_SUSPEND_REQUESTED)
304 return SelfSuspendWait; //Caller should wait for resume
306 return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
309 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
310 STATE_SELF_SUSPENDED: Code should not be running while suspended.
312 STATE_BLOCKING_AND_SUSPENDED: Pool is a local state transition. No VM activities are allowed while in blocking mode.
315 mono_fatal_with_history ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
320 Try to resume a suspended thread.
322 Returns one of the following values:
323 - Sucess: The thread was resumed.
324 - Error: The thread was not suspended in the first place. [2]
325 - InitSelfResume: The thread is blocked on self suspend and should be resumed
326 - InitAsycResume: The thread is blocked on async suspend and should be resumed
327 - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
329 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
330 used as a suspend permit and cancel each other.
332 Suspend permits are really useful to implement managed synchronization structures that
333 don't consume native resources. The downside is that they further complicate the design of this
334 system as the RUNNING state now has a non zero suspend counter.
336 It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
338 One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
339 This would make permits really harder to add.
342 mono_threads_transition_request_resume (MonoThreadInfo* info)
344 int raw_state, cur_state, suspend_count;
345 g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
348 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
350 case STATE_RUNNING: //Thread already running.
351 if (!(suspend_count == 0))
352 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
353 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
354 return ResumeError; //Resume failed because thread was not blocked
356 case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
357 if (suspend_count == 0) {
358 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
361 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
362 goto retry_state_change;
363 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
364 return ResumeOk; //Resume worked and there's nothing for the caller to do.
367 case STATE_ASYNC_SUSPENDED:
368 case STATE_SELF_SUSPENDED:
369 case STATE_BLOCKING_AND_SUSPENDED: //Decrease the suspend_count and maybe resume
370 if (!(suspend_count > 0))
371 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
372 if (suspend_count > 1) {
373 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
374 goto retry_state_change;
375 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
377 return ResumeOk; //Resume worked and there's nothing for the caller to do.
379 if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
380 goto retry_state_change;
381 trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
383 if (cur_state == STATE_ASYNC_SUSPENDED)
384 return ResumeInitAsyncResume; //Resume worked and caller must do async resume
385 else if (cur_state == STATE_SELF_SUSPENDED)
386 return ResumeInitSelfResume; //Resume worked and caller must do self resume
388 return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
391 case STATE_SELF_SUSPEND_REQUESTED: //Self suspend was requested but another thread decided to resume it.
392 if (!(suspend_count > 0))
393 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
394 if (suspend_count > 1) {
395 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
396 goto retry_state_change;
397 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
399 if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
400 goto retry_state_change;
401 trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
403 return ResumeOk; //Resume worked and there's nothing for the caller to do (the target never actually suspend).
406 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
408 [3] A self-resume makes no sense given it requires the thread to be running, which means its suspend count must be zero. A self resume would make
409 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
411 [4] It's questionable on whether a resume (an async operation) should be able to cancel a self suspend. The scenario where this would happen
412 is similar to the one described in [2] when this is used for as a synchronization primitive.
414 If this turns to be a problem we should either implement [2] or make this an invalid transition.
418 mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
423 This performs the last step of async suspend.
425 Returns TRUE if the caller should wait for resume.
428 mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
430 int raw_state, cur_state, suspend_count;
433 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
436 case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
437 case STATE_BLOCKING_AND_SUSPENDED: //async suspend raced with blocking and lost
438 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, 0);
439 return FALSE; //let self suspend wait
441 case STATE_ASYNC_SUSPEND_REQUESTED:
442 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count), raw_state) != raw_state)
443 goto retry_state_change;
444 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, 0);
445 return TRUE; //Async suspend worked, now wait for resume
448 STATE_RUNNING: A thread cannot escape suspension once requested.
449 STATE_ASYNC_SUSPENDED: There can be only one suspend initiator at a given time, meaning this state should have been visible on the first stage of suspend.
450 STATE_SELF_SUSPEND_REQUESTED: When self suspend and async suspend happen together, they converge to async suspend so this state should not be visible.
451 STATE_BLOCKING: Async suspend only begins if a transition to async suspend requested happened. Blocking would have put us into blocking with positive suspend count if it raced with async finish.
454 mono_fatal_with_history ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
459 This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
461 Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
462 That state only works as long as the only managed state touched is blitable and was pinned before the transition.
464 It returns the action the caller must perform:
466 - Continue: Entered blocking state sucessfully;
467 - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
471 mono_threads_transition_do_blocking (MonoThreadInfo* info)
473 int raw_state, cur_state, suspend_count;
476 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
479 case STATE_RUNNING: //transition to blocked
480 if (!(suspend_count == 0))
481 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
482 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count), raw_state) != raw_state)
483 goto retry_state_change;
484 trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, 0);
485 return DoBlockingContinue;
487 case STATE_ASYNC_SUSPEND_REQUESTED:
488 if (!(suspend_count > 0))
489 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
490 trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, 0);
491 return DoBlockingPollAndRetry;
493 STATE_ASYNC_SUSPENDED
494 STATE_SELF_SUSPENDED: Code should not be running while suspended.
495 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
497 STATE_BLOCKING_AND_SUSPENDED: Blocking is not nestabled
500 mono_fatal_with_history ("Cannot transition thread %p from %s with DO_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
505 This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
506 until its resumed before continuing.
509 -Aborted: The blocking operation was aborted and not properly restored. Aborts can happen due to lazy loading and some n2m transitions;
510 -Ok: Done with blocking, just move on;
511 -Wait: This thread was async suspended, wait for resume
514 MonoDoneBlockingResult
515 mono_threads_transition_done_blocking (MonoThreadInfo* info)
517 int raw_state, cur_state, suspend_count;
520 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
522 case STATE_RUNNING: //Blocking was aborted and not properly restored
523 case STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
524 trace_state_change ("DONE_BLOCKING", info, raw_state, cur_state, 0);
525 return DoneBlockingAborted;
528 if (suspend_count == 0) {
529 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
530 goto retry_state_change;
531 trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, 0);
532 return DoneBlockingOk;
534 if (!(suspend_count >= 0))
535 mono_fatal_with_history ("suspend_count = %d, but should be >= 0", suspend_count);
536 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING_AND_SUSPENDED, suspend_count), raw_state) != raw_state)
537 goto retry_state_change;
538 trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_AND_SUSPENDED, 0);
539 return DoneBlockingWait;
543 STATE_ASYNC_SUSPENDED
544 STATE_SELF_SUSPENDED: Code should not be running while suspended.
545 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
546 STATE_BLOCKING_AND_SUSPENDED: This an exit state of done blocking
549 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
554 Transition a thread in what should be a blocking state back to running state.
555 This is different that done blocking because the goal is to get back to blocking once we're done.
556 This is required to be able to bail out of blocking in case we're back to inside the runtime.
559 -Ignore: Thread was not in blocking, nothing to do;
560 -IgnoreAndPool: Thread was not blocking and there's a pending suspend that needs to be processed;
561 -Ok: Blocking state successfully aborted;
562 -Wait: Blocking state successfully aborted, there's a pending suspend to be processed though
564 MonoAbortBlockingResult
565 mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info)
567 int raw_state, cur_state, suspend_count;
570 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
572 case STATE_RUNNING: //thread already in runnable state
573 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
574 return AbortBlockingIgnore;
576 case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
577 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
578 return AbortBlockingIgnoreAndPoll;
581 if (suspend_count == 0) {
582 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
583 goto retry_state_change;
584 trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, 0);
585 return AbortBlockingOk;
587 if (!(suspend_count > 0))
588 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
589 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING_AND_SUSPENDED, suspend_count), raw_state) != raw_state)
590 goto retry_state_change;
591 trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_BLOCKING_AND_SUSPENDED, 0);
592 return AbortBlockingWait;
595 STATE_ASYNC_SUSPENDED:
596 STATE_SELF_SUSPENDED: Code should not be running while suspended.
597 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend.
598 STATE_BLOCKING_AND_SUSPENDED: This is an exit state of done blocking, can't happen here.
601 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
605 MonoThreadUnwindState*
606 mono_thread_info_get_suspend_state (MonoThreadInfo *info)
608 int raw_state, cur_state, suspend_count;
609 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
611 case STATE_ASYNC_SUSPENDED:
612 return &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX];
613 case STATE_SELF_SUSPENDED:
614 case STATE_BLOCKING_AND_SUSPENDED:
615 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
617 if (suspend_count > 0)
618 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
623 STATE_ASYNC_SUSPEND_REQUESTED
624 STATE_BLOCKING: All those are invalid suspend states.
626 g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info), state_name (cur_state));
630 // State checking code
632 * Return TRUE is the thread is in a runnable state.
635 mono_thread_info_is_running (MonoThreadInfo *info)
637 switch (get_thread_state (info->thread_state)) {
639 case STATE_ASYNC_SUSPEND_REQUESTED:
640 case STATE_SELF_SUSPEND_REQUESTED:
648 * Return TRUE is the thread is in an usable (suspendable) state
651 mono_thread_info_is_live (MonoThreadInfo *info)
653 switch (get_thread_state (info->thread_state)) {
662 mono_thread_info_suspend_count (MonoThreadInfo *info)
664 return get_thread_suspend_count (info->thread_state);
668 mono_thread_info_current_state (MonoThreadInfo *info)
670 return get_thread_state (info->thread_state);
674 mono_thread_state_name (int state)
676 return state_name (state);