[2020-02][debugger] Bump protocol for multi threaded single step implementation ...
[mono-project.git] / mono / utils / mono-threads-state-machine.c
blobf1146f5a634eab18db28e9d22f8a74f5d52c40b6
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 int
19 get_thread_state (int thread_state)
21 const MonoThreadStateMachine state = {thread_state};
22 return state.state;
25 #if defined (THREADS_STATE_MACHINE_DEBUG_ENABLED) || defined (ENABLE_CHECKED_BUILD_THREAD)
26 static int
27 get_thread_suspend_count (int thread_state)
29 const MonoThreadStateMachine state = {thread_state};
30 return state.suspend_count;
32 #endif
34 #ifdef THREADS_STATE_MACHINE_DEBUG_ENABLED
35 static gboolean
36 get_thread_no_safepoints (int thread_state)
38 const MonoThreadStateMachine state = {thread_state};
39 return state.no_safepoints;
41 #endif
43 static MonoThreadStateMachine
44 build_thread_state (int thread_state, int suspend_count, gboolean no_safepoints)
46 g_assert (suspend_count >= 0 && suspend_count <= THREAD_SUSPEND_COUNT_MAX);
47 g_assert (thread_state >= 0 && thread_state <= STATE_MAX);
48 no_safepoints = !!no_safepoints; // ensure it's 0 or 1
50 /* need a predictable value for the unused bits so that
51 * thread_state_cas does not fail.
53 MonoThreadStateMachine state = { 0 };
54 state.state = thread_state;
55 state.no_safepoints = no_safepoints;
56 state.suspend_count = suspend_count;
57 return state;
60 static int
61 thread_state_cas (MonoThreadStateMachine *state, MonoThreadStateMachine new_value, int old_raw)
63 return mono_atomic_cas_i32 (&state->raw, new_value.raw, old_raw);
66 static const char*
67 state_name (int state)
69 static const char *state_names [] = {
70 "STARTING",
71 "DETACHED",
73 "RUNNING",
74 "ASYNC_SUSPENDED",
75 "SELF_SUSPENDED",
76 "ASYNC_SUSPEND_REQUESTED",
78 "STATE_BLOCKING",
79 "STATE_BLOCKING_ASYNC_SUSPENDED",
80 "STATE_BLOCKING_SELF_SUSPENDED",
81 "STATE_BLOCKING_SUSPEND_REQUESTED",
83 return state_names [get_thread_state (state)];
86 static void
87 unwrap_thread_state (MonoThreadInfo* info,
88 int *raw,
89 int *cur,
90 int *count,
91 int *blk)
93 g_static_assert (sizeof (MonoThreadStateMachine) == sizeof (int32_t));
94 const MonoThreadStateMachine state = {mono_atomic_load_i32 (&info->thread_state.raw)};
95 // Read once from info and then read from local to get consistent values.
96 *raw = state.raw;
97 *cur = state.state;
98 *count = state.suspend_count;
99 *blk = state.no_safepoints;
102 #define UNWRAP_THREAD_STATE(RAW,CUR,COUNT,BLK,INFO) \
103 unwrap_thread_state ((INFO), &(RAW), &(CUR), &(COUNT), &(BLK))
105 static void
106 check_thread_state (MonoThreadInfo* info)
108 int raw_state, cur_state, suspend_count;
109 gboolean no_safepoints;
110 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
111 switch (cur_state) {
112 case STATE_STARTING:
113 case STATE_DETACHED:
114 g_assert (!no_safepoints);
115 /* fallthru */
116 case STATE_RUNNING:
117 g_assert (suspend_count == 0);
118 break;
119 case STATE_BLOCKING_SELF_SUSPENDED:
120 case STATE_BLOCKING_SUSPEND_REQUESTED:
121 case STATE_BLOCKING_ASYNC_SUSPENDED:
122 case STATE_ASYNC_SUSPENDED:
123 case STATE_SELF_SUSPENDED:
124 g_assert (!no_safepoints);
125 /* fallthru */
126 case STATE_ASYNC_SUSPEND_REQUESTED:
127 g_assert (suspend_count > 0);
128 break;
129 case STATE_BLOCKING:
130 g_assert (!no_safepoints);
131 g_assert (suspend_count == 0);
132 break;
133 default:
134 g_error ("Invalid state %d", cur_state);
138 static void
139 trace_state_change_with_func (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, gboolean next_no_safepoints, int suspend_count_delta, const char *func)
141 check_thread_state (info);
142 THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s %s -> %s %s (%d -> %d) %s\n",
143 transition,
144 mono_thread_info_get_tid (info),
145 state_name (get_thread_state (cur_raw_state)),
146 (get_thread_no_safepoints (cur_raw_state) ? "X" : "."),
147 state_name (next_state),
148 (next_no_safepoints ? "X" : "."),
149 get_thread_suspend_count (cur_raw_state),
150 get_thread_suspend_count (cur_raw_state) + suspend_count_delta,
151 func);
153 CHECKED_BUILD_THREAD_TRANSITION (transition, info, get_thread_state (cur_raw_state), get_thread_suspend_count (cur_raw_state), next_state, suspend_count_delta);
156 static void
157 trace_state_change_sigsafe (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, gboolean next_no_safepoints, int suspend_count_delta, const char *func)
159 check_thread_state (info);
160 THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s %s -> %s %s (%d -> %d) %s\n",
161 transition,
162 mono_thread_info_get_tid (info),
163 state_name (get_thread_state (cur_raw_state)),
164 (get_thread_no_safepoints (cur_raw_state) ? "X" : "."),
165 state_name (next_state),
166 (next_no_safepoints ? "X" : "."),
167 get_thread_suspend_count (cur_raw_state),
168 get_thread_suspend_count (cur_raw_state) + suspend_count_delta,
169 func);
171 CHECKED_BUILD_THREAD_TRANSITION_NOBT (transition, info, get_thread_state (cur_raw_state), get_thread_suspend_count (cur_raw_state), next_state, suspend_count_delta);
174 static void
175 trace_state_change (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, gboolean next_no_safepoints, int suspend_count_delta)
176 // FIXME migrate all uses
178 trace_state_change_with_func (transition, info, cur_raw_state, next_state, next_no_safepoints, suspend_count_delta, "");
182 This is the transition that signals that a thread is functioning.
183 Its main goal is to catch threads been witnessed before been fully registered.
185 void
186 mono_threads_transition_attach (MonoThreadInfo* info)
188 int raw_state, cur_state, suspend_count;
189 gboolean no_safepoints;
191 retry_state_change:
192 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
193 switch (cur_state) {
194 case STATE_STARTING:
195 if (!(suspend_count == 0))
196 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
197 if (no_safepoints)
198 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
199 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, 0, 0), raw_state) != raw_state)
200 goto retry_state_change;
201 trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, FALSE, 0);
202 break;
203 default:
204 mono_fatal_with_history ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
209 This is the transition that signals that a thread is no longer registered with the runtime.
210 Its main goal is to catch threads been witnessed after they detach.
212 This returns TRUE is the transition succeeded.
213 If it returns false it means that there's a pending suspend that should be acted upon.
215 gboolean
216 mono_threads_transition_detach (MonoThreadInfo *info)
218 int raw_state, cur_state, suspend_count;
219 gboolean no_safepoints;
221 retry_state_change:
222 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
223 switch (cur_state) {
224 case STATE_RUNNING:
225 case STATE_BLOCKING: /* An OS thread on coop goes STARTING->BLOCKING->RUNNING->BLOCKING->DETACHED */
226 if (!(suspend_count == 0))
227 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
228 if (no_safepoints)
229 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
230 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_DETACHED, 0, 0), raw_state) != raw_state)
231 goto retry_state_change;
232 trace_state_change ("DETACH", info, raw_state, STATE_DETACHED, FALSE, 0);
233 return TRUE;
234 case STATE_ASYNC_SUSPEND_REQUESTED: //Can't detach until whoever asked us to suspend to be happy with us
235 case STATE_BLOCKING_SUSPEND_REQUESTED:
236 return FALSE;
239 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
240 STATE_SELF_SUSPENDED: Code should not be running while suspended.
241 STATE_BLOCKING_SELF_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
242 STATE_BLOCKING_ASYNC_SUSPENDED: Same as BLOCKING_SELF_SUSPENDED
244 default:
245 mono_fatal_with_history ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
250 This transition initiates the suspension of another thread.
252 Returns one of the following values:
254 - ReqSuspendInitSuspendRunning: Thread suspend requested, caller must initiate suspend.
255 - ReqSuspendInitSuspendBlocking: Thread in blocking state, caller may initiate suspend.
256 - ReqSuspendAlreadySuspended: Thread was already suspended and not executing, nothing to do.
257 - ReqSuspendAlreadySuspendedBlocking: Thread was already in blocking and a suspend was requested
258 and the thread is still executing (perhaps in a syscall),
259 nothing to do.
261 MonoRequestSuspendResult
262 mono_threads_transition_request_suspension (MonoThreadInfo *info)
264 int raw_state, cur_state, suspend_count;
265 gboolean no_safepoints;
266 g_assert (info != mono_thread_info_current ());
268 retry_state_change:
269 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
271 switch (cur_state) {
272 case STATE_RUNNING: //Post an async suspend request
273 if (!(suspend_count == 0))
274 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
275 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1, no_safepoints), raw_state) != raw_state)
276 goto retry_state_change;
277 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, no_safepoints, 1);
278 return ReqSuspendInitSuspendRunning; //This is the first async suspend request against the target
280 case STATE_BLOCKING_SELF_SUSPENDED:
281 case STATE_BLOCKING_ASYNC_SUSPENDED:
282 case STATE_ASYNC_SUSPENDED:
283 case STATE_SELF_SUSPENDED:
284 if (no_safepoints)
285 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
286 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
287 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
288 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count + 1, no_safepoints), raw_state) != raw_state)
289 goto retry_state_change;
290 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, cur_state, no_safepoints, 1);
291 return ReqSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
293 case STATE_BLOCKING:
294 if (!(suspend_count == 0))
295 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
296 if (no_safepoints)
297 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
298 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SUSPEND_REQUESTED, 1, no_safepoints), raw_state) != raw_state)
299 goto retry_state_change;
300 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, STATE_BLOCKING_SUSPEND_REQUESTED, no_safepoints, 1);
301 return ReqSuspendInitSuspendBlocking; //A thread in the blocking state has its state saved so we can treat it as suspended.
302 case STATE_BLOCKING_SUSPEND_REQUESTED:
303 /* This should only be happening if we're doing a cooperative suspend of a blocking thread.
304 * In which case we could be in BLOCKING_SUSPEND_REQUESTED until we execute a done or abort blocking.
305 * In preemptive suspend of a blocking thread since there's a single suspend initiator active at a time,
306 * we would expect a finish_async_suspension or a done/abort blocking before the next suspension request
308 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
309 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
310 if (no_safepoints)
311 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
312 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count + 1, no_safepoints), raw_state) != raw_state)
313 goto retry_state_change;
314 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, cur_state, no_safepoints, 1);
315 return ReqSuspendAlreadySuspendedBlocking;
319 [1] It's questionable on what to do if we hit the beginning of a self suspend.
320 The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
322 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.
324 default:
325 mono_fatal_with_history ("Cannot transition thread %p from %s with SUSPEND_INIT_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
327 return (MonoRequestSuspendResult) FALSE;
332 Peek at the thread state and return whether it's BLOCKING_SUSPEND_REQUESTED or not.
334 Assumes that it is called in the second phase of a two-phase suspend, so the
335 thread is either some flavor of suspended or else blocking suspend requested.
336 All other states can't happen.
338 gboolean
339 mono_threads_transition_peek_blocking_suspend_requested (MonoThreadInfo *info)
341 int raw_state, cur_state, suspend_count;
342 gboolean no_safepoints;
343 g_assert (info != mono_thread_info_current ());
345 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
347 switch (cur_state) {
348 case STATE_ASYNC_SUSPENDED:
349 case STATE_SELF_SUSPENDED:
350 return FALSE; /*ReqPeekBlockingSuspendRequestedRunningSuspended;*/
351 case STATE_BLOCKING_SELF_SUSPENDED:
352 case STATE_BLOCKING_ASYNC_SUSPENDED:
353 case STATE_BLOCKING_SUSPEND_REQUESTED:
354 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
355 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
356 if (no_safepoints)
357 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
358 if (cur_state == STATE_BLOCKING_SUSPEND_REQUESTED)
359 return TRUE; /*ReqPeekBlockingSuspendRequestedBlockingSuspendRequested;*/
360 else
361 return FALSE; /*ReqPeekBlockingSuspendRequestedBlockingSuspended;*/
363 STATE_RUNNING:
364 Can't happen - should have been suspended in the first phase.
365 STATE_ASYNC_SUSPEND_REQUESTED
366 Can't happen - first phase should've waited until the thread self-suspended
367 STATE_BLOCKING:
368 Can't happen - should've had a suspension request in the first phase.
370 default:
371 mono_fatal_with_history ("Thread %p in unexpected state %s with PEEK_BLOCKING_SUSPEND_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
376 Check the current state of the thread and try to init a self suspend.
377 This must be called with self state saved.
379 Returns one of the following values:
381 - Resumed: Async resume happened and current thread should keep running
382 - Suspend: Caller should wait for a resume signal
383 - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
384 suspend should start.
387 MonoSelfSupendResult
388 mono_threads_transition_state_poll (MonoThreadInfo *info)
390 int raw_state, cur_state, suspend_count;
391 gboolean no_safepoints;
392 g_assert (mono_thread_info_is_current (info));
394 retry_state_change:
395 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
396 switch (cur_state) {
397 case STATE_RUNNING:
398 if (no_safepoints)
399 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in RUNNING with STATE_POLL");
400 if (!(suspend_count == 0))
401 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
402 trace_state_change ("STATE_POLL", info, raw_state, cur_state, no_safepoints, 0);
403 return SelfSuspendResumed; //We're fine, don't suspend
405 case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
406 if (no_safepoints)
407 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in ASYNS_SUSPEND_REQUESTED with STATE_POLL");
408 if (!(suspend_count > 0))
409 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
410 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count, no_safepoints), raw_state) != raw_state)
411 goto retry_state_change;
412 trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, no_safepoints, 0);
413 return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
416 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
417 STATE_SELF_SUSPENDED: Code should not be running while suspended.
418 STATE_BLOCKING:
419 STATE_BLOCKING_SUSPEND_REQUESTED:
420 STATE_BLOCKING_ASYNC_SUSPENDED:
421 STATE_BLOCKING_SELF_SUSPENDED: Poll is a local state transition. No VM activities are allowed while in blocking mode.
422 (In all the blocking states - the local thread has no checkpoints, hence
423 no polling, it can only do abort blocking or done blocking on itself).
425 default:
426 mono_fatal_with_history ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
431 Try to resume a suspended thread.
433 Returns one of the following values:
434 - Sucess: The thread was resumed.
435 - Error: The thread was not suspended in the first place. [2]
436 - InitSelfResume: The thread is blocked on self suspend and should be resumed
437 - InitAsyncResume: The thread is blocked on async suspend and should be resumed
438 - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
439 FIXME: ResumeInitBlockingResume is just InitSelfResume by a different name.
441 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
442 used as a suspend permit and cancel each other.
444 Suspend permits are really useful to implement managed synchronization structures that
445 don't consume native resources. The downside is that they further complicate the design of this
446 system as the RUNNING state now has a non zero suspend counter.
448 It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
450 One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
451 This would make permits really harder to add.
453 MonoResumeResult
454 mono_threads_transition_request_resume (MonoThreadInfo* info)
456 int raw_state, cur_state, suspend_count;
457 gboolean no_safepoints;
458 g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
460 retry_state_change:
461 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
462 switch (cur_state) {
463 case STATE_RUNNING: //Thread already running.
464 if (!(suspend_count == 0))
465 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
466 trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, 0);
467 return ResumeError; //Resume failed because thread was not blocked
469 case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
470 if (!(suspend_count == 0))
471 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
472 if (no_safepoints)
473 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
474 trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, 0);
475 return ResumeError;
476 case STATE_BLOCKING_SUSPEND_REQUESTED:
477 if (!(suspend_count > 0))
478 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
479 if (no_safepoints)
480 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
481 if (suspend_count > 1) {
482 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
483 goto retry_state_change;
484 trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, -1);
485 return ResumeOk; //Resume worked and there's nothing for the caller to do.
486 } else {
487 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING, 0, 0), raw_state) != raw_state)
488 goto retry_state_change;
489 trace_state_change ("RESUME", info, raw_state, STATE_BLOCKING, no_safepoints, -1);
490 return ResumeOk; // Resume worked, back in blocking, nothing for the caller to do.
492 case STATE_BLOCKING_ASYNC_SUSPENDED:
493 if (!(suspend_count > 0))
494 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
495 if (no_safepoints)
496 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
497 if (suspend_count > 1) {
498 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
499 goto retry_state_change;
500 trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, -1);
501 return ResumeOk; // Resume worked, there's nothing else for the caller to do.
502 } else {
503 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING, 0, 0), raw_state) != raw_state)
504 goto retry_state_change;
505 trace_state_change ("RESUME", info, raw_state, STATE_BLOCKING, no_safepoints, -1);
506 return ResumeInitAsyncResume; // Resume worked and caller must do async resume, thread resumes in BLOCKING
508 case STATE_BLOCKING_SELF_SUSPENDED: //Decrease the suspend_count and maybe resume
509 case STATE_ASYNC_SUSPENDED:
510 case STATE_SELF_SUSPENDED:
511 if (no_safepoints)
512 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
513 if (!(suspend_count > 0))
514 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
515 if (suspend_count > 1) {
516 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
517 goto retry_state_change;
518 trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, -1);
520 return ResumeOk; //Resume worked and there's nothing for the caller to do.
521 } else {
522 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, 0, no_safepoints), raw_state) != raw_state)
523 goto retry_state_change;
524 trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, no_safepoints, -1);
526 if (cur_state == STATE_ASYNC_SUSPENDED)
527 return ResumeInitAsyncResume; //Resume worked and caller must do async resume
528 else if (cur_state == STATE_SELF_SUSPENDED)
529 return ResumeInitSelfResume; //Resume worked and caller must do self resume
530 else
531 return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
536 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
538 [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
539 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
541 [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
542 is similar to the one described in [2] when this is used for as a synchronization primitive.
544 If this turns to be a problem we should either implement [2] or make this an invalid transition.
547 default:
548 mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
553 Try to resume a suspended thread and atomically request that it suspend again.
555 Returns one of the following values:
556 - InitAsyncPulse: The thread is suspended with preemptive suspend and should be resumed.
558 MonoPulseResult
559 mono_threads_transition_request_pulse (MonoThreadInfo* info)
561 int raw_state, cur_state, suspend_count;
562 gboolean no_safepoints;
563 g_assert (info != mono_thread_info_current ()); //One can't self pulse [3]
565 retry_state_change:
566 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
567 switch (cur_state) {
568 case STATE_BLOCKING_ASYNC_SUSPENDED:
569 if (!(suspend_count == 1))
570 mono_fatal_with_history ("suspend_count = %d, but should be == 1", suspend_count);
571 if (no_safepoints)
572 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
573 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SUSPEND_REQUESTED, suspend_count, no_safepoints), raw_state) != raw_state)
574 goto retry_state_change;
575 trace_state_change ("PULSE", info, raw_state, STATE_BLOCKING_SUSPEND_REQUESTED, no_safepoints, -1);
576 return PulseInitAsyncPulse; // Pulse worked and caller must do async pulse, thread pulses in BLOCKING
579 STATE_RUNNING:
580 STATE_BLOCKING:
581 Only one suspend initiator at a time. Current STW stopped the
582 thread and now needs to resume it. So thread must be in one of the suspended
583 states if we get here.
585 STATE_BLOCKING_SUSPEND_REQUESTED:
586 STATE_ASYNC_SUSPEND_REQUESTED:
587 Only one pulse operation can be in flight, so a pulse cannot witness an
588 internal state of suspend
590 STATE_ASYNC_SUSPENDED:
591 Hybrid suspend shouldn't put GC Unsafe threads into async suspended state.
593 STATE_BLOCKING_SELF_SUSPENDED:
594 STATE_SELF_SUSPENDED:
595 Don't expect these to be pulsed - they're not problematic.
597 default:
598 mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_PULSE", mono_thread_info_get_tid (info), state_name (cur_state));
603 Abort last step of preemptive suspend in case of failure to async suspend thread.
604 This function makes sure state machine reflects current state of thread (running/suspended)
605 in case of failure to complete async suspend of thread. NOTE, thread can still have reached
606 a suspend state (in case of self-suspend).
608 Returns TRUE if async suspend request was successfully aborted. Thread should be in STATE_RUNNING.
609 Returns FALSE if async suspend request was successfully aborted but thread already reached self-suspended.
611 gboolean
612 mono_threads_transition_abort_async_suspend (MonoThreadInfo* info)
614 int raw_state, cur_state, suspend_count;
615 gboolean no_safepoints;
617 retry_state_change:
618 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
619 switch (cur_state) {
620 case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
621 case STATE_BLOCKING_SELF_SUSPENDED: //async suspend raced with blocking and lost
622 if (no_safepoints)
623 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
624 trace_state_change_sigsafe ("ABORT_ASYNC_SUSPEND", info, raw_state, cur_state, no_safepoints, 0, "");
625 return FALSE; //thread successfully reached suspend state.
626 case STATE_ASYNC_SUSPEND_REQUESTED:
627 case STATE_BLOCKING_SUSPEND_REQUESTED:
628 if (!(suspend_count > 0))
629 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
630 if (no_safepoints)
631 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
632 if (suspend_count > 1) {
633 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
634 goto retry_state_change;
635 trace_state_change ("ABORT_ASYNC_SUSPEND", info, raw_state, cur_state, no_safepoints, -1);
636 } else {
637 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, 0, no_safepoints), raw_state) != raw_state)
638 goto retry_state_change;
639 trace_state_change ("ABORT_ASYNC_SUSPEND", info, raw_state, STATE_RUNNING, no_safepoints, -1);
641 return TRUE; //aborting thread suspend request succeded, thread is running.
644 STATE_RUNNING: A thread cannot escape suspension once requested.
645 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.
646 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.
647 With cooperative suspend, there are no finish_async_suspend transitions since there's no path back from asyns_suspend requested to running.
648 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.
650 default:
651 mono_fatal_with_history ("Cannot transition thread %p from %s with ABORT_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
656 This performs the last step of preemptive suspend.
658 Returns TRUE if the caller should wait for resume.
660 gboolean
661 mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
663 int raw_state, cur_state, suspend_count;
664 gboolean no_safepoints;
666 retry_state_change:
667 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
668 switch (cur_state) {
670 case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
671 case STATE_BLOCKING_SELF_SUSPENDED: //async suspend raced with blocking and lost
672 if (no_safepoints)
673 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
674 trace_state_change_sigsafe ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, no_safepoints, 0, "");
675 return FALSE; //let self suspend wait
677 case STATE_ASYNC_SUSPEND_REQUESTED:
678 if (!(suspend_count > 0))
679 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
680 /* Don't expect to see no_safepoints, ever, with async */
681 if (no_safepoints)
682 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in ASYNC_SUSPEND_REQUESTED with FINISH_ASYNC_SUSPEND");
683 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count, FALSE), raw_state) != raw_state)
684 goto retry_state_change;
685 trace_state_change_sigsafe ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, FALSE, 0, "");
686 return TRUE; //Async suspend worked, now wait for resume
687 case STATE_BLOCKING_SUSPEND_REQUESTED:
688 if (!(suspend_count > 0))
689 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
690 if (no_safepoints)
691 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
692 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_ASYNC_SUSPENDED, suspend_count, FALSE), raw_state) != raw_state)
693 goto retry_state_change;
694 trace_state_change_sigsafe ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_BLOCKING_ASYNC_SUSPENDED, FALSE, 0, "");
695 return TRUE; //Async suspend of blocking thread worked, now wait for resume
698 STATE_RUNNING: A thread cannot escape suspension once requested.
699 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.
700 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.
701 With cooperative suspend, there are no finish_async_suspend transitions since there's no path back from asyns_suspend requested to running.
702 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.
704 default:
705 mono_fatal_with_history ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
710 This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
712 Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
713 That state only works as long as the only managed state touched is blitable and was pinned before the transition.
715 It returns the action the caller must perform:
717 - Continue: Entered blocking state sucessfully;
718 - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
721 MonoDoBlockingResult
722 mono_threads_transition_do_blocking (MonoThreadInfo* info, const char *func)
724 int raw_state, cur_state, suspend_count;
725 gboolean no_safepoints;
727 retry_state_change:
728 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
729 switch (cur_state) {
731 case STATE_RUNNING: //transition to blocked
732 if (!(suspend_count == 0))
733 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
734 if (no_safepoints)
735 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in state RUNNING with DO_BLOCKING");
736 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count, no_safepoints), raw_state) != raw_state)
737 goto retry_state_change;
738 trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, no_safepoints, 0);
739 return DoBlockingContinue;
741 case STATE_ASYNC_SUSPEND_REQUESTED:
742 if (!(suspend_count > 0))
743 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
744 if (no_safepoints)
745 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in state ASYNC_SUSPEND_REQUESTED with DO_BLOCKING");
746 trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, no_safepoints, 0);
747 return DoBlockingPollAndRetry;
749 STATE_ASYNC_SUSPENDED
750 STATE_SELF_SUSPENDED: Code should not be running while suspended.
751 STATE_BLOCKING:
752 STATE_BLOCKING_SUSPEND_REQUESTED:
753 STATE_BLOCKING_SELF_SUSPENDED: Blocking is not nestabled
754 STATE_BLOCKING_ASYNC_SUSPENDED: Blocking is not nestable _and_ code should not be running while suspended
756 default:
757 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));
762 This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
763 until its resumed before continuing.
765 It returns one of:
766 -Ok: Done with blocking, just move on;
767 -Wait: This thread was suspended while in blocking, wait for resume.
769 MonoDoneBlockingResult
770 mono_threads_transition_done_blocking (MonoThreadInfo* info, const char *func)
772 int raw_state, cur_state, suspend_count;
773 gboolean no_safepoints;
775 retry_state_change:
776 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
777 switch (cur_state) {
778 case STATE_BLOCKING:
779 if (!(suspend_count == 0))
780 mono_fatal_with_history ("%s suspend_count = %d, but should be == 0", func, suspend_count);
781 if (no_safepoints)
782 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
783 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count, no_safepoints), raw_state) != raw_state)
784 goto retry_state_change;
785 trace_state_change_sigsafe ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, no_safepoints, 0, func);
786 return DoneBlockingOk;
787 case STATE_BLOCKING_SUSPEND_REQUESTED:
788 if (!(suspend_count > 0))
789 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
790 if (no_safepoints)
791 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
792 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SELF_SUSPENDED, suspend_count, no_safepoints), raw_state) != raw_state)
793 goto retry_state_change;
794 trace_state_change_with_func ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_SELF_SUSPENDED, no_safepoints, 0, func);
795 return DoneBlockingWait;
797 STATE_RUNNING: //Blocking was aborted and not properly restored
798 STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
799 STATE_ASYNC_SUSPENDED
800 STATE_SELF_SUSPENDED: Code should not be running while suspended.
801 STATE_BLOCKING_SELF_SUSPENDED: This an exit state of done blocking
802 STATE_BLOCKING_ASYNC_SUSPENDED: This is an exit state of done blocking
804 default:
805 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
810 Transition a thread in what should be a blocking state back to running state.
811 This is different that done blocking because the goal is to get back to blocking once we're done.
812 This is required to be able to bail out of blocking in case we're back to inside the runtime.
814 It returns one of:
815 -Ignore: Thread was not in blocking, nothing to do;
816 -IgnoreAndPoll: Thread was not blocking and there's a pending suspend that needs to be processed;
817 -Ok: Blocking state successfully aborted;
818 -Wait: Blocking state successfully aborted, there's a pending suspend to be processed though, wait for resume.
820 MonoAbortBlockingResult
821 mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info, const char *func)
823 int raw_state, cur_state, suspend_count;
824 gboolean no_safepoints;
826 retry_state_change:
827 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
828 switch (cur_state) {
829 case STATE_RUNNING: //thread already in runnable state
830 /* Even though we're going to ignore this transition, still
831 * assert about no_safepoints. Rationale: make it easier to catch
832 * cases where we would be in ASYNC_SUSPEND_REQUESTED with
833 * no_safepoints set, since those are polling points.
835 /* WISH: make this fatal. Unfortunately in that case, if a
836 * thread asserts somewhere because no_safepoints was set when it
837 * shouldn't have been, we get a second assertion here while
838 * unwinding. */
839 if (no_safepoints)
840 g_warning ("Warning: no_safepoints = TRUE, but should be FALSE in state RUNNING with ABORT_BLOCKING");
841 trace_state_change_sigsafe ("ABORT_BLOCKING", info, raw_state, cur_state, no_safepoints, 0, func);
842 return AbortBlockingIgnore;
844 case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
845 if (no_safepoints)
846 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in state ASYNC_SUSPEND_REQUESTED with ABORT_BLOCKING");
847 trace_state_change_sigsafe ("ABORT_BLOCKING", info, raw_state, cur_state, no_safepoints, 0, func);
848 return AbortBlockingIgnoreAndPoll;
850 case STATE_BLOCKING:
851 if (!(suspend_count == 0))
852 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
853 if (no_safepoints)
854 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
855 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count, FALSE), raw_state) != raw_state)
856 goto retry_state_change;
857 trace_state_change_sigsafe ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, FALSE, 0, func);
858 return AbortBlockingOk;
859 case STATE_BLOCKING_SUSPEND_REQUESTED:
860 if (!(suspend_count > 0))
861 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
862 if (no_safepoints)
863 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
864 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SELF_SUSPENDED, suspend_count, FALSE), raw_state) != raw_state)
865 goto retry_state_change;
866 trace_state_change_with_func ("ABORT_BLOCKING", info, raw_state, STATE_BLOCKING_SELF_SUSPENDED, FALSE, 0, func);
867 return AbortBlockingWait;
869 STATE_ASYNC_SUSPENDED:
870 STATE_SELF_SUSPENDED: Code should not be running while suspended.
871 STATE_BLOCKING_SELF_SUSPENDED: This is an exit state of done blocking, can't happen here.
872 STATE_BLOCKING_ASYNC_SUSPENDED: This is an exit state of abort blocking, can't happen here.
874 default:
875 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
880 Set the no_safepoints flag on an executing GC Unsafe thread.
881 The no_safepoints bit prevents polling (hence self-suspending) and transitioning from GC Unsafe to GC Safe.
882 Thus the thread will not be (cooperatively) interrupted while the bit is set.
884 We don't allow nesting no_safepoints regions, so the flag must be initially unset.
886 Since a suspend initiator may at any time request that a thread should suspend,
887 ASYNC_SUSPEND_REQUESTED is allowed to have the no_safepoints bit set, too.
888 (Future: We could augment this function to return a return value that tells the
889 thread to poll and retry the transition since if we enter here in the
890 ASYNC_SUSPEND_REQUESTED state).
892 void
893 mono_threads_transition_begin_no_safepoints (MonoThreadInfo *info, const char *func)
895 int raw_state, cur_state, suspend_count;
896 gboolean no_safepoints;
898 retry_state_change:
899 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
900 switch (cur_state) {
901 case STATE_RUNNING:
902 case STATE_ASYNC_SUSPEND_REQUESTED:
903 /* Maybe revisit this. But for now, don't allow nesting. */
904 if (no_safepoints)
905 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE with BEGIN_NO_SAFEPOINTS. Can't nest no safepointing regions");
906 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count, TRUE), raw_state) != raw_state)
907 goto retry_state_change;
908 trace_state_change_with_func ("BEGIN_NO_SAFEPOINTS", info, raw_state, cur_state, TRUE, 0, func);
909 return;
911 STATE_STARTING:
912 STATE_DETACHED:
913 STATE_SELF_SUSPENDED:
914 STATE_ASYNC_SUSPENDED:
915 STATE_BLOCKING:
916 STATE_BLOCKING_ASYNC_SUSPENDED:
917 STATE_BLOCKING_SELF_SUSPENDED:
918 STATE_BLOCKING_SUSPEND_REQUESTED:
919 no_safepoints only allowed for threads that are executing and GC Unsafe.
921 default:
922 mono_fatal_with_history ("Cannot transition thread %p from %s with BEGIN_NO_SAFEPOINTS", mono_thread_info_get_tid (info), state_name (cur_state));
927 Unset the no_safepoints flag on an executing GC Unsafe thread.
928 The no_safepoints bit prevents polling (hence self-suspending) and transitioning from GC Unsafe to GC Safe.
929 Thus the thread will not be (cooperatively) interrupted while the bit is set.
931 We don't allow nesting no_safepoints regions, so the flag must be initially set.
933 Since a suspend initiator may at any time request that a thread should suspend,
934 ASYNC_SUSPEND_REQUESTED is allowed to have the no_safepoints bit set, too.
935 (Future: We could augment this function to perform the transition and then
936 return a return value that tells the thread to poll (and safepoint) if we enter
937 here in the ASYNC_SUSPEND_REQUESTED state).
939 void
940 mono_threads_transition_end_no_safepoints (MonoThreadInfo *info, const char *func)
942 int raw_state, cur_state, suspend_count;
943 gboolean no_safepoints;
945 retry_state_change:
946 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
947 switch (cur_state) {
948 case STATE_RUNNING:
949 case STATE_ASYNC_SUSPEND_REQUESTED:
950 if (!no_safepoints)
951 mono_fatal_with_history ("no_safepoints = FALSE, but should be TRUE with END_NO_SAFEPOINTS. Unbalanced no safepointing region");
952 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count, FALSE), raw_state) != raw_state)
953 goto retry_state_change;
954 trace_state_change_with_func ("END_NO_SAFEPOINTS", info, raw_state, cur_state, TRUE, 0, func);
955 return;
957 STATE_STARTING:
958 STATE_DETACHED:
959 STATE_SELF_SUSPENDED:
960 STATE_ASYNC_SUSPENDED:
961 STATE_BLOCKING:
962 STATE_BLOCKING_ASYNC_SUSPENDED:
963 STATE_BLOCKING_SELF_SUSPENDED:
964 STATE_BLOCKING_SUSPEND_REQUESTED:
965 no_safepoints only allowed for threads that are executing and GC Unsafe.
967 default:
968 mono_fatal_with_history ("Cannot transition thread %p from %s with END_NO_SAFEPOINTS", mono_thread_info_get_tid (info), state_name (cur_state));
972 // State checking code
974 * Return TRUE is the thread is in a runnable state.
976 gboolean
977 mono_thread_info_is_running (MonoThreadInfo *info)
979 switch (mono_thread_info_current_state (info)) {
980 case STATE_RUNNING:
981 case STATE_ASYNC_SUSPEND_REQUESTED:
982 case STATE_BLOCKING_SUSPEND_REQUESTED:
983 case STATE_BLOCKING:
984 return TRUE;
986 return FALSE;
990 * Return TRUE is the thread is in an usable (suspendable) state
992 gboolean
993 mono_thread_info_is_live (MonoThreadInfo *info)
995 switch (mono_thread_info_current_state (info)) {
996 case STATE_STARTING:
997 case STATE_DETACHED:
998 return FALSE;
1000 return TRUE;
1004 mono_thread_info_suspend_count (MonoThreadInfo *info)
1006 return info->thread_state.suspend_count;
1010 mono_thread_info_current_state (MonoThreadInfo *info)
1012 return info->thread_state.state;
1015 const char*
1016 mono_thread_state_name (int state)
1018 return state_name (state);
1021 gboolean
1022 mono_thread_is_gc_unsafe_mode (void)
1024 MonoThreadInfo *cur = mono_thread_info_current ();
1026 if (!cur)
1027 return FALSE;
1029 switch (mono_thread_info_current_state (cur)) {
1030 case STATE_RUNNING:
1031 case STATE_ASYNC_SUSPEND_REQUESTED:
1032 return TRUE;
1033 default:
1034 return FALSE;
1038 gboolean
1039 mono_thread_info_will_not_safepoint (MonoThreadInfo *info)
1041 return info->thread_state.no_safepoints;