[threads] Make mono_thread_info_get_suspend_state suspend policy aware
[mono-project.git] / mono / utils / mono-threads-state-machine.c
blob00e486895cb576e5c35cbac8563cc8f6fb06d997
1 /**
2 * \file
3 */
5 #include <config.h>
7 #include <mono/utils/mono-compiler.h>
8 #include <mono/utils/mono-threads.h>
9 #include <mono/utils/mono-tls.h>
10 #include <mono/utils/mono-memory-model.h>
11 #include <mono/utils/atomic.h>
12 #include <mono/utils/checked-build.h>
13 #include <mono/utils/mono-threads-debug.h>
15 #include <errno.h>
17 /*thread state helpers*/
18 static inline int
19 get_thread_state (int thread_state)
21 return thread_state & THREAD_STATE_MASK;
24 static inline int
25 get_thread_suspend_count (int thread_state)
27 return (thread_state & THREAD_SUSPEND_COUNT_MASK) >> THREAD_SUSPEND_COUNT_SHIFT;
30 static inline int
31 build_thread_state (int thread_state, int suspend_count)
33 g_assert (suspend_count >= 0 && suspend_count <= THREAD_SUSPEND_COUNT_MAX);
34 g_assert (thread_state >= 0 && thread_state <= STATE_MAX);
36 return thread_state | (suspend_count << THREAD_SUSPEND_COUNT_SHIFT);
39 static const char*
40 state_name (int state)
42 static const char *state_names [] = {
43 "STARTING",
44 "DETACHED",
46 "RUNNING",
47 "ASYNC_SUSPENDED",
48 "SELF_SUSPENDED",
49 "ASYNC_SUSPEND_REQUESTED",
51 "STATE_BLOCKING",
52 "STATE_BLOCKING_ASYNC_SUSPENDED",
53 "STATE_BLOCKING_SELF_SUSPENDED",
54 "STATE_BLOCKING_SUSPEND_REQUESTED",
56 return state_names [get_thread_state (state)];
59 #define UNWRAP_THREAD_STATE(RAW,CUR,COUNT,INFO) do { \
60 RAW = (INFO)->thread_state; \
61 CUR = get_thread_state (RAW); \
62 COUNT = get_thread_suspend_count (RAW); \
63 } while (0)
65 static void
66 check_thread_state (MonoThreadInfo* info)
68 int raw_state, cur_state, suspend_count;
69 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
70 switch (cur_state) {
71 case STATE_STARTING:
72 case STATE_RUNNING:
73 case STATE_DETACHED:
74 g_assert (suspend_count == 0);
75 break;
76 case STATE_ASYNC_SUSPENDED:
77 case STATE_SELF_SUSPENDED:
78 case STATE_ASYNC_SUSPEND_REQUESTED:
79 case STATE_BLOCKING_SELF_SUSPENDED:
80 case STATE_BLOCKING_SUSPEND_REQUESTED:
81 case STATE_BLOCKING_ASYNC_SUSPENDED:
82 g_assert (suspend_count > 0);
83 break;
84 case STATE_BLOCKING:
85 g_assert (suspend_count == 0);
86 break;
87 default:
88 g_error ("Invalid state %d", cur_state);
92 static inline void
93 trace_state_change_with_func (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, int suspend_count_delta, const char *func)
95 check_thread_state (info);
96 THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s -> %s (%d -> %d) %s\n",
97 transition,
98 mono_thread_info_get_tid (info),
99 state_name (get_thread_state (cur_raw_state)),
100 state_name (next_state),
101 get_thread_suspend_count (cur_raw_state),
102 get_thread_suspend_count (cur_raw_state) + suspend_count_delta,
103 func);
105 CHECKED_BUILD_THREAD_TRANSITION (transition, info, get_thread_state (cur_raw_state), get_thread_suspend_count (cur_raw_state), next_state, suspend_count_delta);
108 static inline void
109 trace_state_change (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, int suspend_count_delta)
110 // FIXME migrate all uses
112 trace_state_change_with_func (transition, info, cur_raw_state, next_state, suspend_count_delta, "");
116 This is the transition that signals that a thread is functioning.
117 Its main goal is to catch threads been witnessed before been fully registered.
119 void
120 mono_threads_transition_attach (MonoThreadInfo* info)
122 int raw_state, cur_state, suspend_count;
124 retry_state_change:
125 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
126 switch (cur_state) {
127 case STATE_STARTING:
128 if (!(suspend_count == 0))
129 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
130 if (mono_atomic_cas_i32 (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
131 goto retry_state_change;
132 trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, 0);
133 break;
134 default:
135 mono_fatal_with_history ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
140 This is the transition that signals that a thread is no longer registered with the runtime.
141 Its main goal is to catch threads been witnessed after they detach.
143 This returns TRUE is the transition succeeded.
144 If it returns false it means that there's a pending suspend that should be acted upon.
146 gboolean
147 mono_threads_transition_detach (MonoThreadInfo *info)
149 int raw_state, cur_state, suspend_count;
151 retry_state_change:
152 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
153 switch (cur_state) {
154 case STATE_RUNNING:
155 case STATE_BLOCKING: /* An OS thread on coop goes STARTING->BLOCKING->RUNNING->BLOCKING->DETACHED */
156 if (!(suspend_count == 0))
157 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
158 if (mono_atomic_cas_i32 (&info->thread_state, STATE_DETACHED, raw_state) != raw_state)
159 goto retry_state_change;
160 trace_state_change ("DETACH", info, raw_state, STATE_DETACHED, 0);
161 return TRUE;
162 case STATE_ASYNC_SUSPEND_REQUESTED: //Can't detach until whoever asked us to suspend to be happy with us
163 case STATE_BLOCKING_SUSPEND_REQUESTED:
164 return FALSE;
167 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
168 STATE_SELF_SUSPENDED: Code should not be running while suspended.
169 STATE_BLOCKING_SELF_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
170 STATE_BLOCKING_ASYNC_SUSPENDED: Same as BLOCKING_SELF_SUSPENDED
172 default:
173 mono_fatal_with_history ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
178 This transition initiates the suspension of another thread.
180 Returns one of the following values:
182 - ReqSuspendInitSuspendRunning: Thread suspend requested, caller must initiate suspend.
183 - ReqSuspendInitSuspendBlocking: Thread in blocking state, caller may initiate suspend.
184 - ReqSuspendAlreadySuspended: Thread was already suspended and not executing, nothing to do.
185 - ReqSuspendAlreadySuspendedBlocking: Thread was already in blocking and a suspend was requested
186 and the thread is still executing (perhaps in a syscall),
187 nothing to do.
189 MonoRequestSuspendResult
190 mono_threads_transition_request_suspension (MonoThreadInfo *info)
192 int raw_state, cur_state, suspend_count;
193 g_assert (info != mono_thread_info_current ());
195 retry_state_change:
196 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
198 switch (cur_state) {
199 case STATE_RUNNING: //Post an async suspend request
200 if (!(suspend_count == 0))
201 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
202 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
203 goto retry_state_change;
204 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
205 return ReqSuspendInitSuspendRunning; //This is the first async suspend request against the target
207 case STATE_ASYNC_SUSPENDED:
208 case STATE_SELF_SUSPENDED:
209 case STATE_BLOCKING_SELF_SUSPENDED:
210 case STATE_BLOCKING_ASYNC_SUSPENDED:
211 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
212 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
213 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
214 goto retry_state_change;
215 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, cur_state, 1);
216 return ReqSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
218 case STATE_BLOCKING:
219 if (!(suspend_count == 0))
220 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
221 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_BLOCKING_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
222 goto retry_state_change;
223 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, STATE_BLOCKING_SUSPEND_REQUESTED, 1);
224 return ReqSuspendInitSuspendBlocking; //A thread in the blocking state has its state saved so we can treat it as suspended.
225 case STATE_BLOCKING_SUSPEND_REQUESTED:
226 /* This should only be happening if we're doing a cooperative suspend of a blocking thread.
227 * In which case we could be in BLOCKING_SUSPEND_REQUESTED until we execute a done or abort blocking.
228 * In preemptive suspend of a blocking thread since there's a single suspend initiator active at a time,
229 * we would expect a finish_async_suspension or a done/abort blocking before the next suspension request
231 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
232 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
233 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
234 goto retry_state_change;
235 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, cur_state, 1);
236 return ReqSuspendAlreadySuspendedBlocking;
240 [1] It's questionable on what to do if we hit the beginning of a self suspend.
241 The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
243 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.
245 default:
246 mono_fatal_with_history ("Cannot transition thread %p from %s with SUSPEND_INIT_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
248 return (MonoRequestSuspendResult) FALSE;
253 Check the current state of the thread and try to init a self suspend.
254 This must be called with self state saved.
256 Returns one of the following values:
258 - Resumed: Async resume happened and current thread should keep running
259 - Suspend: Caller should wait for a resume signal
260 - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
261 suspend should start.
264 MonoSelfSupendResult
265 mono_threads_transition_state_poll (MonoThreadInfo *info)
267 int raw_state, cur_state, suspend_count;
268 g_assert (mono_thread_info_is_current (info));
270 retry_state_change:
271 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
272 switch (cur_state) {
273 case STATE_RUNNING:
274 if (!(suspend_count == 0))
275 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
276 trace_state_change ("STATE_POLL", info, raw_state, cur_state, 0);
277 return SelfSuspendResumed; //We're fine, don't suspend
279 case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
280 if (!(suspend_count > 0))
281 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
282 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
283 goto retry_state_change;
284 trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, 0);
285 return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
288 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
289 STATE_SELF_SUSPENDED: Code should not be running while suspended.
290 STATE_BLOCKING:
291 STATE_BLOCKING_SUSPEND_REQUESTED:
292 STATE_BLOCKING_ASYNC_SUSPENDED:
293 STATE_BLOCKING_SELF_SUSPENDED: Poll is a local state transition. No VM activities are allowed while in blocking mode.
294 (In all the blocking states - the local thread has no checkpoints, hence
295 no polling, it can only do abort blocking or done blocking on itself).
297 default:
298 mono_fatal_with_history ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
303 Try to resume a suspended thread.
305 Returns one of the following values:
306 - Sucess: The thread was resumed.
307 - Error: The thread was not suspended in the first place. [2]
308 - InitSelfResume: The thread is blocked on self suspend and should be resumed
309 - InitAsyncResume: The thread is blocked on async suspend and should be resumed
310 - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
311 FIXME: ResumeInitBlockingResume is just InitSelfResume by a different name.
313 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
314 used as a suspend permit and cancel each other.
316 Suspend permits are really useful to implement managed synchronization structures that
317 don't consume native resources. The downside is that they further complicate the design of this
318 system as the RUNNING state now has a non zero suspend counter.
320 It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
322 One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
323 This would make permits really harder to add.
325 MonoResumeResult
326 mono_threads_transition_request_resume (MonoThreadInfo* info)
328 int raw_state, cur_state, suspend_count;
329 g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
331 retry_state_change:
332 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
333 switch (cur_state) {
334 case STATE_RUNNING: //Thread already running.
335 if (!(suspend_count == 0))
336 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
337 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
338 return ResumeError; //Resume failed because thread was not blocked
340 case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
341 if (!(suspend_count == 0))
342 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
343 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
344 return ResumeError;
345 case STATE_BLOCKING_SUSPEND_REQUESTED:
346 if (!(suspend_count > 0))
347 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
348 if (suspend_count > 1) {
349 if (mono_atomic_cas_i32 (&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.
353 } else {
354 if (mono_atomic_cas_i32 (&info->thread_state, STATE_BLOCKING, raw_state) != raw_state)
355 goto retry_state_change;
356 trace_state_change ("RESUME", info, raw_state, STATE_BLOCKING, -1);
357 return ResumeOk; // Resume worked, back in blocking, nothing for the caller to do.
359 case STATE_BLOCKING_ASYNC_SUSPENDED:
360 if (!(suspend_count > 0))
361 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
362 if (suspend_count > 1) {
363 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
364 goto retry_state_change;
365 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
366 return ResumeOk; // Resume worked, there's nothing else for the caller to do.
367 } else {
368 if (mono_atomic_cas_i32 (&info->thread_state, STATE_BLOCKING, raw_state) != raw_state)
369 goto retry_state_change;
370 trace_state_change ("RESUME", info, raw_state, STATE_BLOCKING, -1);
371 return ResumeInitAsyncResume; // Resume worked and caller must do async resume, thread resumes in BLOCKING
373 case STATE_ASYNC_SUSPENDED:
374 case STATE_SELF_SUSPENDED:
375 case STATE_BLOCKING_SELF_SUSPENDED: //Decrease the suspend_count and maybe resume
376 if (!(suspend_count > 0))
377 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
378 if (suspend_count > 1) {
379 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
380 goto retry_state_change;
381 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
383 return ResumeOk; //Resume worked and there's nothing for the caller to do.
384 } else {
385 if (mono_atomic_cas_i32 (&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 if (cur_state == STATE_ASYNC_SUSPENDED)
390 return ResumeInitAsyncResume; //Resume worked and caller must do async resume
391 else if (cur_state == STATE_SELF_SUSPENDED)
392 return ResumeInitSelfResume; //Resume worked and caller must do self resume
393 else
394 return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
399 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
401 [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
402 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
404 [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
405 is similar to the one described in [2] when this is used for as a synchronization primitive.
407 If this turns to be a problem we should either implement [2] or make this an invalid transition.
410 default:
411 mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
416 This performs the last step of preemptive suspend.
418 Returns TRUE if the caller should wait for resume.
420 gboolean
421 mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
423 int raw_state, cur_state, suspend_count;
425 retry_state_change:
426 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
427 switch (cur_state) {
429 case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
430 case STATE_BLOCKING_SELF_SUSPENDED: //async suspend raced with blocking and lost
431 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, 0);
432 return FALSE; //let self suspend wait
434 case STATE_ASYNC_SUSPEND_REQUESTED:
435 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count), raw_state) != raw_state)
436 goto retry_state_change;
437 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, 0);
438 return TRUE; //Async suspend worked, now wait for resume
439 case STATE_BLOCKING_SUSPEND_REQUESTED:
440 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_BLOCKING_ASYNC_SUSPENDED, suspend_count), raw_state) != raw_state)
441 goto retry_state_change;
442 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_BLOCKING_ASYNC_SUSPENDED, 0);
443 return TRUE; //Async suspend of blocking thread worked, now wait for resume
446 STATE_RUNNING: A thread cannot escape suspension once requested.
447 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.
448 STATE_BLOCKING: If a thread is subject to preemptive suspend, there is no race as the resume initiator should have suspended the thread to STATE_BLOCKING_ASYNC_SUSPENDED or STATE_BLOCKING_SELF_SUSPENDED before resuming.
449 With cooperative suspend, there are no finish_async_suspend transitions since there's no path back from asyns_suspend requested to running.
450 STATE_BLOCKING_ASYNC_SUSPENDED: There can only be one suspend initiator at a given time, meaning this state should have ben visible on the first stage of suspend.
452 default:
453 mono_fatal_with_history ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
458 This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
460 Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
461 That state only works as long as the only managed state touched is blitable and was pinned before the transition.
463 It returns the action the caller must perform:
465 - Continue: Entered blocking state sucessfully;
466 - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
469 MonoDoBlockingResult
470 mono_threads_transition_do_blocking (MonoThreadInfo* info, const char *func)
472 int raw_state, cur_state, suspend_count;
474 retry_state_change:
475 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
476 switch (cur_state) {
478 case STATE_RUNNING: //transition to blocked
479 if (!(suspend_count == 0))
480 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
481 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count), raw_state) != raw_state)
482 goto retry_state_change;
483 trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, 0);
484 return DoBlockingContinue;
486 case STATE_ASYNC_SUSPEND_REQUESTED:
487 if (!(suspend_count > 0))
488 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
489 trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, 0);
490 return DoBlockingPollAndRetry;
492 STATE_ASYNC_SUSPENDED
493 STATE_SELF_SUSPENDED: Code should not be running while suspended.
494 STATE_BLOCKING:
495 STATE_BLOCKING_SUSPEND_REQUESTED:
496 STATE_BLOCKING_SELF_SUSPENDED: Blocking is not nestabled
497 STATE_BLOCKING_ASYNC_SUSPENDED: Blocking is not nestable _and_ code should not be running while suspended
499 default:
500 mono_fatal_with_history ("%s Cannot transition thread %p from %s with DO_BLOCKING", func, 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.
508 It returns one of:
509 -Ok: Done with blocking, just move on;
510 -Wait: This thread was async suspended, wait for resume
511 -NotifyAndWait: This thread was suspended while in blocking, it must notify the initiator if it was suspended preemptively and wait for resume.
513 MonoDoneBlockingResult
514 mono_threads_transition_done_blocking (MonoThreadInfo* info, const char *func)
516 int raw_state, cur_state, suspend_count;
518 retry_state_change:
519 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
520 switch (cur_state) {
521 case STATE_BLOCKING:
522 if (!(suspend_count == 0))
523 mono_fatal_with_history ("%s suspend_count = %d, but should be == 0", func, suspend_count);
524 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
525 goto retry_state_change;
526 trace_state_change_with_func ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, 0, func);
527 return DoneBlockingOk;
528 case STATE_BLOCKING_SUSPEND_REQUESTED:
529 if (!(suspend_count > 0))
530 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
531 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_BLOCKING_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
532 goto retry_state_change;
533 trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_SELF_SUSPENDED, 0);
534 return DoneBlockingNotifyAndWait;
536 STATE_RUNNING: //Blocking was aborted and not properly restored
537 STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
538 STATE_ASYNC_SUSPENDED
539 STATE_SELF_SUSPENDED: Code should not be running while suspended.
540 STATE_BLOCKING_SELF_SUSPENDED: This an exit state of done blocking
541 STATE_BLOCKING_ASYNC_SUSPENDED: This is an exit state of done blocking
543 default:
544 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
549 Transition a thread in what should be a blocking state back to running state.
550 This is different that done blocking because the goal is to get back to blocking once we're done.
551 This is required to be able to bail out of blocking in case we're back to inside the runtime.
553 It returns one of:
554 -Ignore: Thread was not in blocking, nothing to do;
555 -IgnoreAndPool: Thread was not blocking and there's a pending suspend that needs to be processed;
556 -Ok: Blocking state successfully aborted;
557 -Wait: Blocking state successfully aborted, there's a pending suspend to be processed though
558 -NotifyAndWait: Blocking state was successfully aborted but the thread was preemptively suspended while in blocking, it must notify the initiator and wait for resume.
560 MonoAbortBlockingResult
561 mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info)
563 int raw_state, cur_state, suspend_count;
565 retry_state_change:
566 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
567 switch (cur_state) {
568 case STATE_RUNNING: //thread already in runnable state
569 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
570 return AbortBlockingIgnore;
572 case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
573 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
574 return AbortBlockingIgnoreAndPoll;
576 case STATE_BLOCKING:
577 if (!(suspend_count == 0))
578 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
579 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
580 goto retry_state_change;
581 trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, 0);
582 return AbortBlockingOk;
583 case STATE_BLOCKING_SUSPEND_REQUESTED:
584 if (!(suspend_count > 0))
585 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
586 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_BLOCKING_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
587 goto retry_state_change;
588 trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_BLOCKING_SELF_SUSPENDED, 0);
589 return AbortBlockingNotifyAndWait;
591 STATE_ASYNC_SUSPENDED:
592 STATE_SELF_SUSPENDED: Code should not be running while suspended.
593 STATE_BLOCKING_SELF_SUSPENDED: This is an exit state of done blocking, can't happen here.
594 STATE_BLOCKING_ASYNC_SUSPENDED: This is an exit state of abort blocking, can't happen here.
596 default:
597 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
601 // State checking code
603 * Return TRUE is the thread is in a runnable state.
605 gboolean
606 mono_thread_info_is_running (MonoThreadInfo *info)
608 switch (get_thread_state (info->thread_state)) {
609 case STATE_RUNNING:
610 case STATE_ASYNC_SUSPEND_REQUESTED:
611 case STATE_BLOCKING_SUSPEND_REQUESTED:
612 case STATE_BLOCKING:
613 return TRUE;
615 return FALSE;
619 * Return TRUE is the thread is in an usable (suspendable) state
621 gboolean
622 mono_thread_info_is_live (MonoThreadInfo *info)
624 switch (get_thread_state (info->thread_state)) {
625 case STATE_STARTING:
626 case STATE_DETACHED:
627 return FALSE;
629 return TRUE;
633 mono_thread_info_suspend_count (MonoThreadInfo *info)
635 return get_thread_suspend_count (info->thread_state);
639 mono_thread_info_current_state (MonoThreadInfo *info)
641 return get_thread_state (info->thread_state);
644 const char*
645 mono_thread_state_name (int state)
647 return state_name (state);
650 gboolean
651 mono_thread_is_gc_unsafe_mode (void)
653 MonoThreadInfo *cur = mono_thread_info_current ();
655 if (!cur)
656 return FALSE;
658 switch (mono_thread_info_current_state (cur)) {
659 case STATE_RUNNING:
660 case STATE_ASYNC_SUSPEND_REQUESTED:
661 return TRUE;
662 default:
663 return FALSE;