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 g_error ("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 g_error ("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 g_error ("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.
206 MonoRequestAsyncSuspendResult
207 mono_threads_transition_request_async_suspension (MonoThreadInfo
*info
)
209 int raw_state
, cur_state
, suspend_count
;
210 g_assert (info
!= mono_thread_info_current ());
213 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, info
);
216 case STATE_RUNNING
: //Post an async suspend request
217 g_assert (suspend_count
== 0);
218 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED
, 1), raw_state
) != raw_state
)
219 goto retry_state_change
;
220 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info
, raw_state
, STATE_ASYNC_SUSPEND_REQUESTED
, 1);
221 return AsyncSuspendInitSuspend
; //This is the first async suspend request against the target
223 case STATE_ASYNC_SUSPENDED
:
224 case STATE_SELF_SUSPENDED
: //Async suspend can suspend the same thread multiple times as it starts from the outside
225 case STATE_BLOCKING_AND_SUSPENDED
:
226 g_assert (suspend_count
> 0 && suspend_count
< THREAD_SUSPEND_COUNT_MAX
);
227 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (cur_state
, suspend_count
+ 1), raw_state
) != raw_state
)
228 goto retry_state_change
;
229 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info
, raw_state
, cur_state
, 1);
230 return AsyncSuspendAlreadySuspended
; //Thread is already suspended so we don't need to wait it to suspend
232 case STATE_SELF_SUSPEND_REQUESTED
: //This suspend needs to notify the initiator, so we need to promote the suspend to async
233 g_assert (suspend_count
> 0 && suspend_count
< THREAD_SUSPEND_COUNT_MAX
);
234 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED
, suspend_count
+ 1), raw_state
) != raw_state
)
235 goto retry_state_change
;
236 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info
, raw_state
, STATE_ASYNC_SUSPEND_REQUESTED
, 1);
237 return AsyncSuspendWait
; //This is the first async suspend request, change the thread and let it notify us [1]
240 g_assert (suspend_count
< THREAD_SUSPEND_COUNT_MAX
);
241 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (cur_state
, suspend_count
+ 1), raw_state
) != raw_state
)
242 goto retry_state_change
;
243 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info
, raw_state
, cur_state
, 1);
244 return AsyncSuspendAlreadySuspended
; //A thread in the blocking state has its state saved so we can treat it as suspended.
248 [1] It's questionable on what to do if we hit the beginning of a self suspend.
249 The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
251 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.
254 g_error ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", mono_thread_info_get_tid (info
), state_name (cur_state
));
256 return (MonoRequestAsyncSuspendResult
) FALSE
;
260 Check the current state of the thread and try to init a self suspend.
261 This must be called with self state saved.
263 Returns one of the following values:
265 - Resumed: Async resume happened and current thread should keep running
266 - Suspend: Caller should wait for a resume signal
267 - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
268 suspend should start.
272 mono_threads_transition_state_poll (MonoThreadInfo
*info
)
274 int raw_state
, cur_state
, suspend_count
;
275 g_assert (info
== mono_thread_info_current ());
278 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, info
);
281 g_assert (suspend_count
== 0);
282 trace_state_change ("STATE_POLL", info
, raw_state
, cur_state
, 0);
283 return SelfSuspendResumed
; //We're fine, don't suspend
285 case STATE_ASYNC_SUSPEND_REQUESTED
: //Async suspend requested, service it with a self suspend
286 case STATE_SELF_SUSPEND_REQUESTED
: //Start the self suspend process
287 g_assert (suspend_count
> 0);
288 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (STATE_SELF_SUSPENDED
, suspend_count
), raw_state
) != raw_state
)
289 goto retry_state_change
;
290 trace_state_change ("STATE_POLL", info
, raw_state
, STATE_SELF_SUSPENDED
, 0);
291 if (cur_state
== STATE_SELF_SUSPEND_REQUESTED
)
292 return SelfSuspendWait
; //Caller should wait for resume
294 return SelfSuspendNotifyAndWait
; //Caller should notify suspend initiator and wait for resume
297 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
298 STATE_SELF_SUSPENDED: Code should not be running while suspended.
300 STATE_BLOCKING_AND_SUSPENDED: Pool is a local state transition. No VM activities are allowed while in blocking mode.
303 g_error ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info
), state_name (cur_state
));
308 Try to resume a suspended thread.
310 Returns one of the following values:
311 - Sucess: The thread was resumed.
312 - Error: The thread was not suspended in the first place. [2]
313 - InitSelfResume: The thread is blocked on self suspend and should be resumed
314 - InitAsycResume: The thread is blocked on async suspend and should be resumed
315 - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
317 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
318 used as a suspend permit and cancel each other.
320 Suspend permits are really useful to implement managed synchronization structures that
321 don't consume native resources. The downside is that they further complicate the design of this
322 system as the RUNNING state now has a non zero suspend counter.
324 It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
326 One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
327 This would make permits really harder to add.
330 mono_threads_transition_request_resume (MonoThreadInfo
* info
)
332 int raw_state
, cur_state
, suspend_count
;
333 g_assert (info
!= mono_thread_info_current ()); //One can't self resume [3]
336 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, info
);
338 case STATE_RUNNING
: //Thread already running.
339 g_assert (suspend_count
== 0);
340 trace_state_change ("RESUME", info
, raw_state
, cur_state
, 0);
341 return ResumeError
; //Resume failed because thread was not blocked
343 case STATE_BLOCKING
: //Blocking, might have a suspend count, we decrease if it's > 0
344 if (suspend_count
== 0) {
345 trace_state_change ("RESUME", info
, raw_state
, cur_state
, 0);
348 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (cur_state
, suspend_count
- 1), raw_state
) != raw_state
)
349 goto retry_state_change
;
350 trace_state_change ("RESUME", info
, raw_state
, cur_state
, -1);
351 return ResumeOk
; //Resume worked and there's nothing for the caller to do.
354 case STATE_ASYNC_SUSPENDED
:
355 case STATE_SELF_SUSPENDED
:
356 case STATE_BLOCKING_AND_SUSPENDED
: //Decrease the suspend_count and maybe resume
357 g_assert (suspend_count
> 0);
358 if (suspend_count
> 1) {
359 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (cur_state
, suspend_count
- 1), raw_state
) != raw_state
)
360 goto retry_state_change
;
361 trace_state_change ("RESUME", info
, raw_state
, cur_state
, -1);
363 return ResumeOk
; //Resume worked and there's nothing for the caller to do.
365 if (InterlockedCompareExchange (&info
->thread_state
, STATE_RUNNING
, raw_state
) != raw_state
)
366 goto retry_state_change
;
367 trace_state_change ("RESUME", info
, raw_state
, STATE_RUNNING
, -1);
369 if (cur_state
== STATE_ASYNC_SUSPENDED
)
370 return ResumeInitAsyncResume
; //Resume worked and caller must do async resume
371 else if (cur_state
== STATE_SELF_SUSPENDED
)
372 return ResumeInitSelfResume
; //Resume worked and caller must do self resume
374 return ResumeInitBlockingResume
; //Resume worked and caller must do blocking resume
377 case STATE_SELF_SUSPEND_REQUESTED
: //Self suspend was requested but another thread decided to resume it.
378 g_assert (suspend_count
> 0);
379 if (suspend_count
> 1) {
380 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (cur_state
, suspend_count
- 1), raw_state
) != raw_state
)
381 goto retry_state_change
;
382 trace_state_change ("RESUME", info
, raw_state
, cur_state
, -1);
384 if (InterlockedCompareExchange (&info
->thread_state
, STATE_RUNNING
, raw_state
) != raw_state
)
385 goto retry_state_change
;
386 trace_state_change ("RESUME", info
, raw_state
, STATE_RUNNING
, -1);
388 return ResumeOk
; //Resume worked and there's nothing for the caller to do (the target never actually suspend).
391 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
393 [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
394 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
396 [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
397 is similar to the one described in [2] when this is used for as a synchronization primitive.
399 If this turns to be a problem we should either implement [2] or make this an invalid transition.
403 g_error ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info
), state_name (cur_state
));
408 This performs the last step of async suspend.
410 Returns TRUE if the caller should wait for resume.
413 mono_threads_transition_finish_async_suspend (MonoThreadInfo
* info
)
415 int raw_state
, cur_state
, suspend_count
;
418 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, info
);
421 case STATE_SELF_SUSPENDED
: //async suspend raced with self suspend and lost
422 case STATE_BLOCKING_AND_SUSPENDED
: //async suspend raced with blocking and lost
423 trace_state_change ("FINISH_ASYNC_SUSPEND", info
, raw_state
, cur_state
, 0);
424 return FALSE
; //let self suspend wait
426 case STATE_ASYNC_SUSPEND_REQUESTED
:
427 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (STATE_ASYNC_SUSPENDED
, suspend_count
), raw_state
) != raw_state
)
428 goto retry_state_change
;
429 trace_state_change ("FINISH_ASYNC_SUSPEND", info
, raw_state
, STATE_ASYNC_SUSPENDED
, 0);
430 return TRUE
; //Async suspend worked, now wait for resume
433 STATE_RUNNING: A thread cannot escape suspension once requested.
434 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.
435 STATE_SELF_SUSPEND_REQUESTED: When self suspend and async suspend happen together, they converge to async suspend so this state should not be visible.
436 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.
439 g_error ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info
), state_name (cur_state
));
444 This the compensatory transition for failed async suspend.
446 Async suspend can land on a thread as it began cleaning up and is no longer
447 functional. This happens as cleanup is a racy process from the async suspend
448 perspective. The thread could have cleaned up its domain or jit_tls, for example.
450 It can only transition the state as left by a sucessfull finish async suspend transition.
454 mono_threads_transition_async_suspend_compensation (MonoThreadInfo
* info
)
456 int raw_state
, cur_state
, suspend_count
;
459 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, info
);
462 case STATE_ASYNC_SUSPENDED
:
464 Must be one since if a self suspend is in progress the thread should still be async suspendable.
465 If count > 1 and no self suspend is in progress then it means one of the following two.
466 - the thread was previously suspended, which means we should never reach end suspend in the first place.
467 - another suspend happened concurrently, which means the global suspend lock didn't happen.
469 g_assert (suspend_count
== 1);
470 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (STATE_RUNNING
, suspend_count
- 1), raw_state
) != raw_state
)
471 goto retry_state_change
;
472 trace_state_change ("COMPENSATE_FINISH_ASYNC_SUSPEND", info
, raw_state
, STATE_RUNNING
, -1);
477 STATE_ASYNC_SUSPEND_REQUESTED
479 STATE_BLOCKING_AND_SUSPENDED
480 STATE_SELF_SUSPEND_REQUESTED: All those are invalid end states of a sucessfull finish async suspend
483 g_error ("Cannot transition thread %p from %s with COMPENSATE_FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info
), state_name (cur_state
));
489 This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
491 Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
492 That state only works as long as the only managed state touched is blitable and was pinned before the transition.
494 It returns the action the caller must perform:
496 - Continue: Entered blocking state sucessfully;
497 - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
501 mono_threads_transition_do_blocking (MonoThreadInfo
* info
)
503 int raw_state
, cur_state
, suspend_count
;
506 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, info
);
509 case STATE_RUNNING
: //transition to blocked
510 g_assert (suspend_count
== 0);
511 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (STATE_BLOCKING
, suspend_count
), raw_state
) != raw_state
)
512 goto retry_state_change
;
513 trace_state_change ("DO_BLOCKING", info
, raw_state
, STATE_BLOCKING
, 0);
514 return DoBlockingContinue
;
516 case STATE_ASYNC_SUSPEND_REQUESTED
:
517 g_assert (suspend_count
> 0);
518 trace_state_change ("DO_BLOCKING", info
, raw_state
, cur_state
, 0);
519 return DoBlockingPollAndRetry
;
521 STATE_ASYNC_SUSPENDED
522 STATE_SELF_SUSPENDED: Code should not be running while suspended.
523 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
525 STATE_BLOCKING_AND_SUSPENDED: Blocking is not nestabled
528 g_error ("Cannot transition thread %p from %s with DO_BLOCKING", mono_thread_info_get_tid (info
), state_name (cur_state
));
533 This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
534 until its resumed before continuing.
537 -Aborted: The blocking operation was aborted and not properly restored. Aborts can happen due to lazy loading and some n2m transitions;
538 -Ok: Done with blocking, just move on;
539 -Wait: This thread was async suspended, wait for resume
542 MonoDoneBlockingResult
543 mono_threads_transition_done_blocking (MonoThreadInfo
* info
)
545 int raw_state
, cur_state
, suspend_count
;
548 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, info
);
550 case STATE_RUNNING
: //Blocking was aborted and not properly restored
551 case STATE_ASYNC_SUSPEND_REQUESTED
: //Blocking was aborted, not properly restored and now there's a pending suspend
552 trace_state_change ("DONE_BLOCKING", info
, raw_state
, cur_state
, 0);
553 return DoneBlockingAborted
;
556 if (suspend_count
== 0) {
557 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (STATE_RUNNING
, suspend_count
), raw_state
) != raw_state
)
558 goto retry_state_change
;
559 trace_state_change ("DONE_BLOCKING", info
, raw_state
, STATE_RUNNING
, 0);
560 return DoneBlockingOk
;
562 g_assert (suspend_count
>= 0);
563 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (STATE_BLOCKING_AND_SUSPENDED
, suspend_count
), raw_state
) != raw_state
)
564 goto retry_state_change
;
565 trace_state_change ("DONE_BLOCKING", info
, raw_state
, STATE_BLOCKING_AND_SUSPENDED
, 0);
566 return DoneBlockingWait
;
570 STATE_ASYNC_SUSPENDED
571 STATE_SELF_SUSPENDED: Code should not be running while suspended.
572 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
573 STATE_BLOCKING_AND_SUSPENDED: This an exit state of done blocking
576 g_error ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info
), state_name (cur_state
));
581 Transition a thread in what should be a blocking state back to running state.
582 This is different that done blocking because the goal is to get back to blocking once we're done.
583 This is required to be able to bail out of blocking in case we're back to inside the runtime.
586 -Ignore: Thread was not in blocking, nothing to do;
587 -IgnoreAndPool: Thread was not blocking and there's a pending suspend that needs to be processed;
588 -Ok: Blocking state successfully aborted;
589 -OkAndPool: Blocking state successfully aborted, there's a pending suspend to be processed though
591 MonoAbortBlockingResult
592 mono_threads_transition_abort_blocking (THREAD_INFO_TYPE
* info
)
594 int raw_state
, cur_state
, suspend_count
;
597 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, info
);
599 case STATE_RUNNING
: //thread already in runnable state
600 trace_state_change ("ABORT_BLOCKING", info
, raw_state
, cur_state
, 0);
601 return AbortBlockingIgnore
;
603 case STATE_ASYNC_SUSPEND_REQUESTED
: //thread is runnable and have a pending suspend
604 trace_state_change ("ABORT_BLOCKING", info
, raw_state
, cur_state
, 0);
605 return AbortBlockingIgnoreAndPoll
;
608 if (suspend_count
== 0) {
609 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (STATE_RUNNING
, suspend_count
), raw_state
) != raw_state
)
610 goto retry_state_change
;
611 trace_state_change ("ABORT_BLOCKING", info
, raw_state
, STATE_RUNNING
, 0);
612 return AbortBlockingOk
;
614 if (InterlockedCompareExchange (&info
->thread_state
, build_thread_state (STATE_SELF_SUSPEND_REQUESTED
, suspend_count
), raw_state
) != raw_state
)
615 goto retry_state_change
;
616 trace_state_change ("ABORT_BLOCKING", info
, raw_state
, STATE_SELF_SUSPEND_REQUESTED
, 0);
617 return AbortBlockingOkAndPool
;
620 STATE_ASYNC_SUSPENDED:
621 STATE_SELF_SUSPENDED: Code should not be running while suspended.
622 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend.
623 STATE_BLOCKING_AND_SUSPENDED: This is an exit state of done blocking, can't happen here.
626 g_error ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info
), state_name (cur_state
));
630 MonoThreadUnwindState
*
631 mono_thread_info_get_suspend_state (MonoThreadInfo
*info
)
633 int raw_state
, cur_state
, suspend_count
;
634 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, info
);
636 case STATE_ASYNC_SUSPENDED
:
637 return &info
->thread_saved_state
[ASYNC_SUSPEND_STATE_INDEX
];
638 case STATE_SELF_SUSPENDED
:
639 case STATE_BLOCKING_AND_SUSPENDED
:
640 return &info
->thread_saved_state
[SELF_SUSPEND_STATE_INDEX
];
642 if (suspend_count
> 0)
643 return &info
->thread_saved_state
[SELF_SUSPEND_STATE_INDEX
];
648 STATE_ASYNC_SUSPEND_REQUESTED
649 STATE_BLOCKING: All those are invalid suspend states.
651 g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info
), state_name (cur_state
));
655 // State checking code
657 * Return TRUE is the thread is in a runnable state.
660 mono_thread_info_is_running (MonoThreadInfo
*info
)
662 switch (get_thread_state (info
->thread_state
)) {
664 case STATE_ASYNC_SUSPEND_REQUESTED
:
665 case STATE_SELF_SUSPEND_REQUESTED
:
673 * Return TRUE is the thread is in an usable (suspendable) state
676 mono_thread_info_is_live (MonoThreadInfo
*info
)
678 switch (get_thread_state (info
->thread_state
)) {
687 mono_thread_info_suspend_count (MonoThreadInfo
*info
)
689 return get_thread_suspend_count (info
->thread_state
);
693 mono_thread_info_current_state (MonoThreadInfo
*info
)
695 return get_thread_state (info
->thread_state
);
699 mono_thread_state_name (int state
)
701 return state_name (state
);