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 g_assert (suspend_count == 0);
111 if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
112 goto retry_state_change;
113 trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, 0);
116 mono_fatal_with_history ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
121 This is the transition that signals that a thread is no longer registered with the runtime.
122 Its main goal is to catch threads been witnessed after they detach.
124 This returns TRUE is the transition succeeded.
125 If it returns false it means that there's a pending suspend that should be acted upon.
128 mono_threads_transition_detach (MonoThreadInfo *info)
130 int raw_state, cur_state, suspend_count;
133 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
136 g_assert (suspend_count == 0);
137 if (InterlockedCompareExchange (&info->thread_state, STATE_DETACHED, raw_state) != raw_state)
138 goto retry_state_change;
139 trace_state_change ("DETACH", info, raw_state, STATE_DETACHED, 0);
141 case STATE_ASYNC_SUSPEND_REQUESTED: //Can't detach until whoever asked us to suspend to be happy with us
144 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
145 STATE_SELF_SUSPENDED: Code should not be running while suspended.
146 STATE_SELF_SUSPEND_REQUESTED: This is a bug in the self suspend code that didn't execute the second part of it
147 STATE_BLOCKING: This is a bug in the coop code that forgot to do a finish blocking before exiting.
148 STATE_BLOCKING_AND_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
151 mono_fatal_with_history ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
156 This transition initiates the suspension of the current thread.
159 mono_threads_transition_request_self_suspension (MonoThreadInfo *info)
161 int raw_state, cur_state, suspend_count;
162 g_assert (info == mono_thread_info_current ());
165 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
168 case STATE_RUNNING: //Post a self suspend request
169 g_assert (suspend_count == 0);
170 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
171 goto retry_state_change;
172 trace_state_change ("SELF_SUSPEND_REQUEST", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 1);
175 case STATE_ASYNC_SUSPEND_REQUESTED: //Bump the suspend count but don't change the request type as async takes preference
176 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
177 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
178 goto retry_state_change;
179 trace_state_change ("SUSPEND_REQUEST", info, raw_state, cur_state, 1);
183 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
184 STATE_SELF_SUSPENDED: Code should not be running while suspended.
185 STATE_SELF_SUSPEND_REQUESTED: Self suspends should not nest as begin/end should be paired. [1]
187 STATE_BLOCKING_AND_SUSPENDED: Self suspension cannot be started when the thread is in blocking state as it must finish first
189 [1] This won't trap this sequence of requests: self suspend, async suspend and self suspend.
190 If this turns to be an issue we can introduce a new suspend request state for when both have been requested.
193 mono_fatal_with_history ("Cannot transition thread %p from %s with SUSPEND_REQUEST", mono_thread_info_get_tid (info), state_name (cur_state));
198 This transition initiates the suspension of another thread.
200 Returns one of the following values:
202 - AsyncSuspendInitSuspend: Thread suspend requested, async suspend needs to be done.
203 - AsyncSuspendAlreadySuspended: Thread already suspended, nothing to do.
204 - AsyncSuspendWait: Self suspend in progress, asked it to notify us. Caller must add target to the notification set.
205 - AsyncSuspendBlocking: Thread in blocking state
207 MonoRequestAsyncSuspendResult
208 mono_threads_transition_request_async_suspension (MonoThreadInfo *info)
210 int raw_state, cur_state, suspend_count;
211 g_assert (info != mono_thread_info_current ());
214 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
217 case STATE_RUNNING: //Post an async suspend request
218 g_assert (suspend_count == 0);
219 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
220 goto retry_state_change;
221 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
222 return AsyncSuspendInitSuspend; //This is the first async suspend request against the target
224 case STATE_ASYNC_SUSPENDED:
225 case STATE_SELF_SUSPENDED: //Async suspend can suspend the same thread multiple times as it starts from the outside
226 case STATE_BLOCKING_AND_SUSPENDED:
227 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
228 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
229 goto retry_state_change;
230 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
231 return AsyncSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
233 case STATE_SELF_SUSPEND_REQUESTED: //This suspend needs to notify the initiator, so we need to promote the suspend to async
234 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
235 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, suspend_count + 1), raw_state) != raw_state)
236 goto retry_state_change;
237 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
238 return AsyncSuspendWait; //This is the first async suspend request, change the thread and let it notify us [1]
241 g_assert (suspend_count < THREAD_SUSPEND_COUNT_MAX);
242 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
243 goto retry_state_change;
244 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
245 return AsyncSuspendBlocking; //A thread in the blocking state has its state saved so we can treat it as suspended.
249 [1] It's questionable on what to do if we hit the beginning of a self suspend.
250 The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
252 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.
255 mono_fatal_with_history ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
257 return (MonoRequestAsyncSuspendResult) FALSE;
261 Check the current state of the thread and try to init a self suspend.
262 This must be called with self state saved.
264 Returns one of the following values:
266 - Resumed: Async resume happened and current thread should keep running
267 - Suspend: Caller should wait for a resume signal
268 - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
269 suspend should start.
273 mono_threads_transition_state_poll (MonoThreadInfo *info)
275 int raw_state, cur_state, suspend_count;
276 g_assert (info == mono_thread_info_current ());
279 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
282 g_assert (suspend_count == 0);
283 trace_state_change ("STATE_POLL", info, raw_state, cur_state, 0);
284 return SelfSuspendResumed; //We're fine, don't suspend
286 case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
287 case STATE_SELF_SUSPEND_REQUESTED: //Start the self suspend process
288 g_assert (suspend_count > 0);
289 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
290 goto retry_state_change;
291 trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, 0);
292 if (cur_state == STATE_SELF_SUSPEND_REQUESTED)
293 return SelfSuspendWait; //Caller should wait for resume
295 return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
298 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
299 STATE_SELF_SUSPENDED: Code should not be running while suspended.
301 STATE_BLOCKING_AND_SUSPENDED: Pool is a local state transition. No VM activities are allowed while in blocking mode.
304 mono_fatal_with_history ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
309 Try to resume a suspended thread.
311 Returns one of the following values:
312 - Sucess: The thread was resumed.
313 - Error: The thread was not suspended in the first place. [2]
314 - InitSelfResume: The thread is blocked on self suspend and should be resumed
315 - InitAsycResume: The thread is blocked on async suspend and should be resumed
316 - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
318 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
319 used as a suspend permit and cancel each other.
321 Suspend permits are really useful to implement managed synchronization structures that
322 don't consume native resources. The downside is that they further complicate the design of this
323 system as the RUNNING state now has a non zero suspend counter.
325 It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
327 One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
328 This would make permits really harder to add.
331 mono_threads_transition_request_resume (MonoThreadInfo* info)
333 int raw_state, cur_state, suspend_count;
334 g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
337 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
339 case STATE_RUNNING: //Thread already running.
340 g_assert (suspend_count == 0);
341 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
342 return ResumeError; //Resume failed because thread was not blocked
344 case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
345 if (suspend_count == 0) {
346 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
349 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
350 goto retry_state_change;
351 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
352 return ResumeOk; //Resume worked and there's nothing for the caller to do.
355 case STATE_ASYNC_SUSPENDED:
356 case STATE_SELF_SUSPENDED:
357 case STATE_BLOCKING_AND_SUSPENDED: //Decrease the suspend_count and maybe resume
358 g_assert (suspend_count > 0);
359 if (suspend_count > 1) {
360 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
361 goto retry_state_change;
362 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
364 return ResumeOk; //Resume worked and there's nothing for the caller to do.
366 if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
367 goto retry_state_change;
368 trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
370 if (cur_state == STATE_ASYNC_SUSPENDED)
371 return ResumeInitAsyncResume; //Resume worked and caller must do async resume
372 else if (cur_state == STATE_SELF_SUSPENDED)
373 return ResumeInitSelfResume; //Resume worked and caller must do self resume
375 return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
378 case STATE_SELF_SUSPEND_REQUESTED: //Self suspend was requested but another thread decided to resume it.
379 g_assert (suspend_count > 0);
380 if (suspend_count > 1) {
381 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
382 goto retry_state_change;
383 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
385 if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
386 goto retry_state_change;
387 trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
389 return ResumeOk; //Resume worked and there's nothing for the caller to do (the target never actually suspend).
392 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
394 [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
395 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
397 [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
398 is similar to the one described in [2] when this is used for as a synchronization primitive.
400 If this turns to be a problem we should either implement [2] or make this an invalid transition.
404 mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
409 This performs the last step of async suspend.
411 Returns TRUE if the caller should wait for resume.
414 mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
416 int raw_state, cur_state, suspend_count;
419 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
422 case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
423 case STATE_BLOCKING_AND_SUSPENDED: //async suspend raced with blocking and lost
424 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, 0);
425 return FALSE; //let self suspend wait
427 case STATE_ASYNC_SUSPEND_REQUESTED:
428 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count), raw_state) != raw_state)
429 goto retry_state_change;
430 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, 0);
431 return TRUE; //Async suspend worked, now wait for resume
434 STATE_RUNNING: A thread cannot escape suspension once requested.
435 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.
436 STATE_SELF_SUSPEND_REQUESTED: When self suspend and async suspend happen together, they converge to async suspend so this state should not be visible.
437 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.
440 mono_fatal_with_history ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
445 This the compensatory transition for failed async suspend.
447 Async suspend can land on a thread as it began cleaning up and is no longer
448 functional. This happens as cleanup is a racy process from the async suspend
449 perspective. The thread could have cleaned up its domain or jit_tls, for example.
451 It can only transition the state as left by a sucessfull finish async suspend transition.
455 mono_threads_transition_async_suspend_compensation (MonoThreadInfo* info)
457 int raw_state, cur_state, suspend_count;
460 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
463 case STATE_ASYNC_SUSPENDED:
465 Must be one since if a self suspend is in progress the thread should still be async suspendable.
466 If count > 1 and no self suspend is in progress then it means one of the following two.
467 - the thread was previously suspended, which means we should never reach end suspend in the first place.
468 - another suspend happened concurrently, which means the global suspend lock didn't happen.
470 g_assert (suspend_count == 1);
471 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count - 1), raw_state) != raw_state)
472 goto retry_state_change;
473 trace_state_change ("COMPENSATE_FINISH_ASYNC_SUSPEND", info, raw_state, STATE_RUNNING, -1);
478 STATE_ASYNC_SUSPEND_REQUESTED
480 STATE_BLOCKING_AND_SUSPENDED
481 STATE_SELF_SUSPEND_REQUESTED: All those are invalid end states of a sucessfull finish async suspend
484 mono_fatal_with_history ("Cannot transition thread %p from %s with COMPENSATE_FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
490 This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
492 Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
493 That state only works as long as the only managed state touched is blitable and was pinned before the transition.
495 It returns the action the caller must perform:
497 - Continue: Entered blocking state sucessfully;
498 - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
502 mono_threads_transition_do_blocking (MonoThreadInfo* info)
504 int raw_state, cur_state, suspend_count;
507 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
510 case STATE_RUNNING: //transition to blocked
511 g_assert (suspend_count == 0);
512 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count), raw_state) != raw_state)
513 goto retry_state_change;
514 trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, 0);
515 return DoBlockingContinue;
517 case STATE_ASYNC_SUSPEND_REQUESTED:
518 g_assert (suspend_count > 0);
519 trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, 0);
520 return DoBlockingPollAndRetry;
522 STATE_ASYNC_SUSPENDED
523 STATE_SELF_SUSPENDED: Code should not be running while suspended.
524 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
526 STATE_BLOCKING_AND_SUSPENDED: Blocking is not nestabled
529 mono_fatal_with_history ("Cannot transition thread %p from %s with DO_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
534 This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
535 until its resumed before continuing.
538 -Aborted: The blocking operation was aborted and not properly restored. Aborts can happen due to lazy loading and some n2m transitions;
539 -Ok: Done with blocking, just move on;
540 -Wait: This thread was async suspended, wait for resume
543 MonoDoneBlockingResult
544 mono_threads_transition_done_blocking (MonoThreadInfo* info)
546 int raw_state, cur_state, suspend_count;
549 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
551 case STATE_RUNNING: //Blocking was aborted and not properly restored
552 case STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
553 trace_state_change ("DONE_BLOCKING", info, raw_state, cur_state, 0);
554 return DoneBlockingAborted;
557 if (suspend_count == 0) {
558 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
559 goto retry_state_change;
560 trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, 0);
561 return DoneBlockingOk;
563 g_assert (suspend_count >= 0);
564 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING_AND_SUSPENDED, suspend_count), raw_state) != raw_state)
565 goto retry_state_change;
566 trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_AND_SUSPENDED, 0);
567 return DoneBlockingWait;
571 STATE_ASYNC_SUSPENDED
572 STATE_SELF_SUSPENDED: Code should not be running while suspended.
573 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
574 STATE_BLOCKING_AND_SUSPENDED: This an exit state of done blocking
577 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
582 Transition a thread in what should be a blocking state back to running state.
583 This is different that done blocking because the goal is to get back to blocking once we're done.
584 This is required to be able to bail out of blocking in case we're back to inside the runtime.
587 -Ignore: Thread was not in blocking, nothing to do;
588 -IgnoreAndPool: Thread was not blocking and there's a pending suspend that needs to be processed;
589 -Ok: Blocking state successfully aborted;
590 -OkAndPool: Blocking state successfully aborted, there's a pending suspend to be processed though
592 MonoAbortBlockingResult
593 mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info)
595 int raw_state, cur_state, suspend_count;
598 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
600 case STATE_RUNNING: //thread already in runnable state
601 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
602 return AbortBlockingIgnore;
604 case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
605 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
606 return AbortBlockingIgnoreAndPoll;
609 if (suspend_count == 0) {
610 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
611 goto retry_state_change;
612 trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, 0);
613 return AbortBlockingOk;
615 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPEND_REQUESTED, suspend_count), raw_state) != raw_state)
616 goto retry_state_change;
617 trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 0);
618 return AbortBlockingOkAndPool;
621 STATE_ASYNC_SUSPENDED:
622 STATE_SELF_SUSPENDED: Code should not be running while suspended.
623 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend.
624 STATE_BLOCKING_AND_SUSPENDED: This is an exit state of done blocking, can't happen here.
627 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
631 MonoThreadUnwindState*
632 mono_thread_info_get_suspend_state (MonoThreadInfo *info)
634 int raw_state, cur_state, suspend_count;
635 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
637 case STATE_ASYNC_SUSPENDED:
638 return &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX];
639 case STATE_SELF_SUSPENDED:
640 case STATE_BLOCKING_AND_SUSPENDED:
641 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
643 if (suspend_count > 0)
644 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
649 STATE_ASYNC_SUSPEND_REQUESTED
650 STATE_BLOCKING: All those are invalid suspend states.
652 g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info), state_name (cur_state));
656 // State checking code
658 * Return TRUE is the thread is in a runnable state.
661 mono_thread_info_is_running (MonoThreadInfo *info)
663 switch (get_thread_state (info->thread_state)) {
665 case STATE_ASYNC_SUSPEND_REQUESTED:
666 case STATE_SELF_SUSPEND_REQUESTED:
674 * Return TRUE is the thread is in an usable (suspendable) state
677 mono_thread_info_is_live (MonoThreadInfo *info)
679 switch (get_thread_state (info->thread_state)) {
688 mono_thread_info_suspend_count (MonoThreadInfo *info)
690 return get_thread_suspend_count (info->thread_state);
694 mono_thread_info_current_state (MonoThreadInfo *info)
696 return get_thread_state (info->thread_state);
700 mono_thread_state_name (int state)
702 return state_name (state);