[coop] Use bitfields for coop state machine state representation (#17298)
[mono-project.git] / mono / utils / mono-threads-state-machine.c
blob5c357a0ff949375f7b8dec819057c02ca2bfa04c
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 MonoThreadStateMachine state;
22 state.raw = thread_state;
23 return state.state;
26 static int
27 get_thread_suspend_count (int thread_state)
29 MonoThreadStateMachine state;
30 state.raw = thread_state;
31 return state.suspend_count;
34 static gboolean
35 get_thread_no_safepoints (int thread_state)
37 MonoThreadStateMachine state;
38 state.raw = thread_state;
39 return state.no_safepoints;
42 static int
43 build_thread_state (int thread_state, int suspend_count, gboolean no_safepoints)
45 g_assert (suspend_count >= 0 && suspend_count <= THREAD_SUSPEND_COUNT_MAX);
46 g_assert (thread_state >= 0 && thread_state <= STATE_MAX);
47 no_safepoints = !!no_safepoints; // ensure it's 0 or 1
49 MonoThreadStateMachine state;
50 /* want a predictable value for the unused bits so that
51 * thread_state_cas doesn't fail for spurious reasons.
53 state.raw = 0;
54 state.state = thread_state;
55 state.no_safepoints = no_safepoints;
56 state.suspend_count = suspend_count;
57 return state.raw;
60 static int
61 thread_state_cas (MonoThreadStateMachine *state, int new_raw, int old_raw)
63 return mono_atomic_cas_i32 (&state->raw, new_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 MonoThreadStateMachine state;
94 state.raw = mono_atomic_load_i32 (&info->thread_state.raw);
95 /* read once from info and then read from state so we unpack
96 * consistently */
97 *raw = state.raw;
98 *cur = state.state;
99 *count = state.suspend_count;
100 *blk = state.no_safepoints;
103 #define UNWRAP_THREAD_STATE(RAW,CUR,COUNT,BLK,INFO) \
104 unwrap_thread_state ((INFO), &(RAW), &(CUR), &(COUNT), &(BLK))
106 static void
107 check_thread_state (MonoThreadInfo* info)
109 int raw_state, cur_state, suspend_count;
110 gboolean no_safepoints;
111 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
112 switch (cur_state) {
113 case STATE_STARTING:
114 case STATE_DETACHED:
115 g_assert (!no_safepoints);
116 /* fallthru */
117 case STATE_RUNNING:
118 g_assert (suspend_count == 0);
119 break;
120 case STATE_BLOCKING_SELF_SUSPENDED:
121 case STATE_BLOCKING_SUSPEND_REQUESTED:
122 case STATE_BLOCKING_ASYNC_SUSPENDED:
123 case STATE_ASYNC_SUSPENDED:
124 case STATE_SELF_SUSPENDED:
125 g_assert (!no_safepoints);
126 /* fallthru */
127 case STATE_ASYNC_SUSPEND_REQUESTED:
128 g_assert (suspend_count > 0);
129 break;
130 case STATE_BLOCKING:
131 g_assert (!no_safepoints);
132 g_assert (suspend_count == 0);
133 break;
134 default:
135 g_error ("Invalid state %d", cur_state);
139 static void
140 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)
142 check_thread_state (info);
143 THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s %s -> %s %s (%d -> %d) %s\n",
144 transition,
145 mono_thread_info_get_tid (info),
146 state_name (get_thread_state (cur_raw_state)),
147 (get_thread_no_safepoints (cur_raw_state) ? "X" : "."),
148 state_name (next_state),
149 (next_no_safepoints ? "X" : "."),
150 get_thread_suspend_count (cur_raw_state),
151 get_thread_suspend_count (cur_raw_state) + suspend_count_delta,
152 func);
154 CHECKED_BUILD_THREAD_TRANSITION (transition, info, get_thread_state (cur_raw_state), get_thread_suspend_count (cur_raw_state), next_state, suspend_count_delta);
157 static void
158 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)
160 check_thread_state (info);
161 THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s %s -> %s %s (%d -> %d) %s\n",
162 transition,
163 mono_thread_info_get_tid (info),
164 state_name (get_thread_state (cur_raw_state)),
165 (get_thread_no_safepoints (cur_raw_state) ? "X" : "."),
166 state_name (next_state),
167 (next_no_safepoints ? "X" : "."),
168 get_thread_suspend_count (cur_raw_state),
169 get_thread_suspend_count (cur_raw_state) + suspend_count_delta,
170 func);
172 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);
175 static void
176 trace_state_change (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, gboolean next_no_safepoints, int suspend_count_delta)
177 // FIXME migrate all uses
179 trace_state_change_with_func (transition, info, cur_raw_state, next_state, next_no_safepoints, suspend_count_delta, "");
183 This is the transition that signals that a thread is functioning.
184 Its main goal is to catch threads been witnessed before been fully registered.
186 void
187 mono_threads_transition_attach (MonoThreadInfo* info)
189 int raw_state, cur_state, suspend_count;
190 gboolean no_safepoints;
192 retry_state_change:
193 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
194 switch (cur_state) {
195 case STATE_STARTING:
196 if (!(suspend_count == 0))
197 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
198 if (no_safepoints)
199 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
200 if (thread_state_cas (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
201 goto retry_state_change;
202 trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, FALSE, 0);
203 break;
204 default:
205 mono_fatal_with_history ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
210 This is the transition that signals that a thread is no longer registered with the runtime.
211 Its main goal is to catch threads been witnessed after they detach.
213 This returns TRUE is the transition succeeded.
214 If it returns false it means that there's a pending suspend that should be acted upon.
216 gboolean
217 mono_threads_transition_detach (MonoThreadInfo *info)
219 int raw_state, cur_state, suspend_count;
220 gboolean no_safepoints;
222 retry_state_change:
223 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
224 switch (cur_state) {
225 case STATE_RUNNING:
226 case STATE_BLOCKING: /* An OS thread on coop goes STARTING->BLOCKING->RUNNING->BLOCKING->DETACHED */
227 if (!(suspend_count == 0))
228 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
229 if (no_safepoints)
230 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
231 if (thread_state_cas (&info->thread_state, STATE_DETACHED, raw_state) != raw_state)
232 goto retry_state_change;
233 trace_state_change ("DETACH", info, raw_state, STATE_DETACHED, FALSE, 0);
234 return TRUE;
235 case STATE_ASYNC_SUSPEND_REQUESTED: //Can't detach until whoever asked us to suspend to be happy with us
236 case STATE_BLOCKING_SUSPEND_REQUESTED:
237 return FALSE;
240 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
241 STATE_SELF_SUSPENDED: Code should not be running while suspended.
242 STATE_BLOCKING_SELF_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
243 STATE_BLOCKING_ASYNC_SUSPENDED: Same as BLOCKING_SELF_SUSPENDED
245 default:
246 mono_fatal_with_history ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
251 This transition initiates the suspension of another thread.
253 Returns one of the following values:
255 - ReqSuspendInitSuspendRunning: Thread suspend requested, caller must initiate suspend.
256 - ReqSuspendInitSuspendBlocking: Thread in blocking state, caller may initiate suspend.
257 - ReqSuspendAlreadySuspended: Thread was already suspended and not executing, nothing to do.
258 - ReqSuspendAlreadySuspendedBlocking: Thread was already in blocking and a suspend was requested
259 and the thread is still executing (perhaps in a syscall),
260 nothing to do.
262 MonoRequestSuspendResult
263 mono_threads_transition_request_suspension (MonoThreadInfo *info)
265 int raw_state, cur_state, suspend_count;
266 gboolean no_safepoints;
267 g_assert (info != mono_thread_info_current ());
269 retry_state_change:
270 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
272 switch (cur_state) {
273 case STATE_RUNNING: //Post an async suspend request
274 if (!(suspend_count == 0))
275 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
276 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1, no_safepoints), raw_state) != raw_state)
277 goto retry_state_change;
278 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, no_safepoints, 1);
279 return ReqSuspendInitSuspendRunning; //This is the first async suspend request against the target
281 case STATE_BLOCKING_SELF_SUSPENDED:
282 case STATE_BLOCKING_ASYNC_SUSPENDED:
283 case STATE_ASYNC_SUSPENDED:
284 case STATE_SELF_SUSPENDED:
285 if (no_safepoints)
286 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
287 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
288 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
289 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count + 1, no_safepoints), raw_state) != raw_state)
290 goto retry_state_change;
291 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, cur_state, no_safepoints, 1);
292 return ReqSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
294 case STATE_BLOCKING:
295 if (!(suspend_count == 0))
296 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
297 if (no_safepoints)
298 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
299 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SUSPEND_REQUESTED, 1, no_safepoints), raw_state) != raw_state)
300 goto retry_state_change;
301 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, STATE_BLOCKING_SUSPEND_REQUESTED, no_safepoints, 1);
302 return ReqSuspendInitSuspendBlocking; //A thread in the blocking state has its state saved so we can treat it as suspended.
303 case STATE_BLOCKING_SUSPEND_REQUESTED:
304 /* This should only be happening if we're doing a cooperative suspend of a blocking thread.
305 * In which case we could be in BLOCKING_SUSPEND_REQUESTED until we execute a done or abort blocking.
306 * In preemptive suspend of a blocking thread since there's a single suspend initiator active at a time,
307 * we would expect a finish_async_suspension or a done/abort blocking before the next suspension request
309 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
310 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
311 if (no_safepoints)
312 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
313 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count + 1, no_safepoints), raw_state) != raw_state)
314 goto retry_state_change;
315 trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, cur_state, no_safepoints, 1);
316 return ReqSuspendAlreadySuspendedBlocking;
320 [1] It's questionable on what to do if we hit the beginning of a self suspend.
321 The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
323 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.
325 default:
326 mono_fatal_with_history ("Cannot transition thread %p from %s with SUSPEND_INIT_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
328 return (MonoRequestSuspendResult) FALSE;
333 Peek at the thread state and return whether it's BLOCKING_SUSPEND_REQUESTED or not.
335 Assumes that it is called in the second phase of a two-phase suspend, so the
336 thread is either some flavor of suspended or else blocking suspend requested.
337 All other states can't happen.
339 gboolean
340 mono_threads_transition_peek_blocking_suspend_requested (MonoThreadInfo *info)
342 int raw_state, cur_state, suspend_count;
343 gboolean no_safepoints;
344 g_assert (info != mono_thread_info_current ());
346 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
348 switch (cur_state) {
349 case STATE_ASYNC_SUSPENDED:
350 case STATE_SELF_SUSPENDED:
351 return FALSE; /*ReqPeekBlockingSuspendRequestedRunningSuspended;*/
352 case STATE_BLOCKING_SELF_SUSPENDED:
353 case STATE_BLOCKING_ASYNC_SUSPENDED:
354 case STATE_BLOCKING_SUSPEND_REQUESTED:
355 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
356 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
357 if (no_safepoints)
358 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
359 if (cur_state == STATE_BLOCKING_SUSPEND_REQUESTED)
360 return TRUE; /*ReqPeekBlockingSuspendRequestedBlockingSuspendRequested;*/
361 else
362 return FALSE; /*ReqPeekBlockingSuspendRequestedBlockingSuspended;*/
364 STATE_RUNNING:
365 Can't happen - should have been suspended in the first phase.
366 STATE_ASYNC_SUSPEND_REQUESTED
367 Can't happen - first phase should've waited until the thread self-suspended
368 STATE_BLOCKING:
369 Can't happen - should've had a suspension request in the first phase.
371 default:
372 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));
377 Check the current state of the thread and try to init a self suspend.
378 This must be called with self state saved.
380 Returns one of the following values:
382 - Resumed: Async resume happened and current thread should keep running
383 - Suspend: Caller should wait for a resume signal
384 - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
385 suspend should start.
388 MonoSelfSupendResult
389 mono_threads_transition_state_poll (MonoThreadInfo *info)
391 int raw_state, cur_state, suspend_count;
392 gboolean no_safepoints;
393 g_assert (mono_thread_info_is_current (info));
395 retry_state_change:
396 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
397 switch (cur_state) {
398 case STATE_RUNNING:
399 if (no_safepoints)
400 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in RUNNING with STATE_POLL");
401 if (!(suspend_count == 0))
402 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
403 trace_state_change ("STATE_POLL", info, raw_state, cur_state, no_safepoints, 0);
404 return SelfSuspendResumed; //We're fine, don't suspend
406 case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
407 if (no_safepoints)
408 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in ASYNS_SUSPEND_REQUESTED with STATE_POLL");
409 if (!(suspend_count > 0))
410 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
411 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count, no_safepoints), raw_state) != raw_state)
412 goto retry_state_change;
413 trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, no_safepoints, 0);
414 return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
417 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
418 STATE_SELF_SUSPENDED: Code should not be running while suspended.
419 STATE_BLOCKING:
420 STATE_BLOCKING_SUSPEND_REQUESTED:
421 STATE_BLOCKING_ASYNC_SUSPENDED:
422 STATE_BLOCKING_SELF_SUSPENDED: Poll is a local state transition. No VM activities are allowed while in blocking mode.
423 (In all the blocking states - the local thread has no checkpoints, hence
424 no polling, it can only do abort blocking or done blocking on itself).
426 default:
427 mono_fatal_with_history ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
432 Try to resume a suspended thread.
434 Returns one of the following values:
435 - Sucess: The thread was resumed.
436 - Error: The thread was not suspended in the first place. [2]
437 - InitSelfResume: The thread is blocked on self suspend and should be resumed
438 - InitAsyncResume: The thread is blocked on async suspend and should be resumed
439 - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
440 FIXME: ResumeInitBlockingResume is just InitSelfResume by a different name.
442 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
443 used as a suspend permit and cancel each other.
445 Suspend permits are really useful to implement managed synchronization structures that
446 don't consume native resources. The downside is that they further complicate the design of this
447 system as the RUNNING state now has a non zero suspend counter.
449 It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
451 One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
452 This would make permits really harder to add.
454 MonoResumeResult
455 mono_threads_transition_request_resume (MonoThreadInfo* info)
457 int raw_state, cur_state, suspend_count;
458 gboolean no_safepoints;
459 g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
461 retry_state_change:
462 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
463 switch (cur_state) {
464 case STATE_RUNNING: //Thread already running.
465 if (!(suspend_count == 0))
466 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
467 trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, 0);
468 return ResumeError; //Resume failed because thread was not blocked
470 case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
471 if (!(suspend_count == 0))
472 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
473 if (no_safepoints)
474 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
475 trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, 0);
476 return ResumeError;
477 case STATE_BLOCKING_SUSPEND_REQUESTED:
478 if (!(suspend_count > 0))
479 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
480 if (no_safepoints)
481 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
482 if (suspend_count > 1) {
483 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
484 goto retry_state_change;
485 trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, -1);
486 return ResumeOk; //Resume worked and there's nothing for the caller to do.
487 } else {
488 if (thread_state_cas (&info->thread_state, STATE_BLOCKING, raw_state) != raw_state)
489 goto retry_state_change;
490 trace_state_change ("RESUME", info, raw_state, STATE_BLOCKING, no_safepoints, -1);
491 return ResumeOk; // Resume worked, back in blocking, nothing for the caller to do.
493 case STATE_BLOCKING_ASYNC_SUSPENDED:
494 if (!(suspend_count > 0))
495 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
496 if (no_safepoints)
497 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
498 if (suspend_count > 1) {
499 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
500 goto retry_state_change;
501 trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, -1);
502 return ResumeOk; // Resume worked, there's nothing else for the caller to do.
503 } else {
504 if (thread_state_cas (&info->thread_state, STATE_BLOCKING, raw_state) != raw_state)
505 goto retry_state_change;
506 trace_state_change ("RESUME", info, raw_state, STATE_BLOCKING, no_safepoints, -1);
507 return ResumeInitAsyncResume; // Resume worked and caller must do async resume, thread resumes in BLOCKING
509 case STATE_BLOCKING_SELF_SUSPENDED: //Decrease the suspend_count and maybe resume
510 case STATE_ASYNC_SUSPENDED:
511 case STATE_SELF_SUSPENDED:
512 if (no_safepoints)
513 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
514 if (!(suspend_count > 0))
515 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
516 if (suspend_count > 1) {
517 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
518 goto retry_state_change;
519 trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, -1);
521 return ResumeOk; //Resume worked and there's nothing for the caller to do.
522 } else {
523 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, 0, no_safepoints), raw_state) != raw_state)
524 goto retry_state_change;
525 trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, no_safepoints, -1);
527 if (cur_state == STATE_ASYNC_SUSPENDED)
528 return ResumeInitAsyncResume; //Resume worked and caller must do async resume
529 else if (cur_state == STATE_SELF_SUSPENDED)
530 return ResumeInitSelfResume; //Resume worked and caller must do self resume
531 else
532 return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
537 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
539 [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
540 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
542 [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
543 is similar to the one described in [2] when this is used for as a synchronization primitive.
545 If this turns to be a problem we should either implement [2] or make this an invalid transition.
548 default:
549 mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
554 Try to resume a suspended thread and atomically request that it suspend again.
556 Returns one of the following values:
557 - InitAsyncPulse: The thread is suspended with preemptive suspend and should be resumed.
559 MonoPulseResult
560 mono_threads_transition_request_pulse (MonoThreadInfo* info)
562 int raw_state, cur_state, suspend_count;
563 gboolean no_safepoints;
564 g_assert (info != mono_thread_info_current ()); //One can't self pulse [3]
566 retry_state_change:
567 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
568 switch (cur_state) {
569 case STATE_BLOCKING_ASYNC_SUSPENDED:
570 if (!(suspend_count == 1))
571 mono_fatal_with_history ("suspend_count = %d, but should be == 1", suspend_count);
572 if (no_safepoints)
573 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
574 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SUSPEND_REQUESTED, suspend_count, no_safepoints), raw_state) != raw_state)
575 goto retry_state_change;
576 trace_state_change ("PULSE", info, raw_state, STATE_BLOCKING_SUSPEND_REQUESTED, no_safepoints, -1);
577 return PulseInitAsyncPulse; // Pulse worked and caller must do async pulse, thread pulses in BLOCKING
580 STATE_RUNNING:
581 STATE_BLOCKING:
582 Only one suspend initiator at a time. Current STW stopped the
583 thread and now needs to resume it. So thread must be in one of the suspended
584 states if we get here.
586 STATE_BLOCKING_SUSPEND_REQUESTED:
587 STATE_ASYNC_SUSPEND_REQUESTED:
588 Only one pulse operation can be in flight, so a pulse cannot witness an
589 internal state of suspend
591 STATE_ASYNC_SUSPENDED:
592 Hybrid suspend shouldn't put GC Unsafe threads into async suspended state.
594 STATE_BLOCKING_SELF_SUSPENDED:
595 STATE_SELF_SUSPENDED:
596 Don't expect these to be pulsed - they're not problematic.
598 default:
599 mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_PULSE", mono_thread_info_get_tid (info), state_name (cur_state));
604 Abort last step of preemptive suspend in case of failure to async suspend thread.
605 This function makes sure state machine reflects current state of thread (running/suspended)
606 in case of failure to complete async suspend of thread. NOTE, thread can still have reached
607 a suspend state (in case of self-suspend).
609 Returns TRUE if async suspend request was successfully aborted. Thread should be in STATE_RUNNING.
610 Returns FALSE if async suspend request was successfully aborted but thread already reached self-suspended.
612 gboolean
613 mono_threads_transition_abort_async_suspend (MonoThreadInfo* info)
615 int raw_state, cur_state, suspend_count;
616 gboolean no_safepoints;
618 retry_state_change:
619 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
620 switch (cur_state) {
621 case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
622 case STATE_BLOCKING_SELF_SUSPENDED: //async suspend raced with blocking and lost
623 if (no_safepoints)
624 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
625 trace_state_change_sigsafe ("ABORT_ASYNC_SUSPEND", info, raw_state, cur_state, no_safepoints, 0, "");
626 return FALSE; //thread successfully reached suspend state.
627 case STATE_ASYNC_SUSPEND_REQUESTED:
628 case STATE_BLOCKING_SUSPEND_REQUESTED:
629 if (!(suspend_count > 0))
630 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
631 if (no_safepoints)
632 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
633 if (suspend_count > 1) {
634 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
635 goto retry_state_change;
636 trace_state_change ("ABORT_ASYNC_SUSPEND", info, raw_state, cur_state, no_safepoints, -1);
637 } else {
638 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, 0, no_safepoints), raw_state) != raw_state)
639 goto retry_state_change;
640 trace_state_change ("ABORT_ASYNC_SUSPEND", info, raw_state, STATE_RUNNING, no_safepoints, -1);
642 return TRUE; //aborting thread suspend request succeded, thread is running.
645 STATE_RUNNING: A thread cannot escape suspension once requested.
646 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.
647 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.
648 With cooperative suspend, there are no finish_async_suspend transitions since there's no path back from asyns_suspend requested to running.
649 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.
651 default:
652 mono_fatal_with_history ("Cannot transition thread %p from %s with ABORT_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
657 This performs the last step of preemptive suspend.
659 Returns TRUE if the caller should wait for resume.
661 gboolean
662 mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
664 int raw_state, cur_state, suspend_count;
665 gboolean no_safepoints;
667 retry_state_change:
668 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
669 switch (cur_state) {
671 case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
672 case STATE_BLOCKING_SELF_SUSPENDED: //async suspend raced with blocking and lost
673 if (no_safepoints)
674 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
675 trace_state_change_sigsafe ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, no_safepoints, 0, "");
676 return FALSE; //let self suspend wait
678 case STATE_ASYNC_SUSPEND_REQUESTED:
679 if (!(suspend_count > 0))
680 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
681 /* Don't expect to see no_safepoints, ever, with async */
682 if (no_safepoints)
683 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in ASYNC_SUSPEND_REQUESTED with FINISH_ASYNC_SUSPEND");
684 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count, FALSE), raw_state) != raw_state)
685 goto retry_state_change;
686 trace_state_change_sigsafe ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, FALSE, 0, "");
687 return TRUE; //Async suspend worked, now wait for resume
688 case STATE_BLOCKING_SUSPEND_REQUESTED:
689 if (!(suspend_count > 0))
690 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
691 if (no_safepoints)
692 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
693 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_ASYNC_SUSPENDED, suspend_count, FALSE), raw_state) != raw_state)
694 goto retry_state_change;
695 trace_state_change_sigsafe ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_BLOCKING_ASYNC_SUSPENDED, FALSE, 0, "");
696 return TRUE; //Async suspend of blocking thread worked, now wait for resume
699 STATE_RUNNING: A thread cannot escape suspension once requested.
700 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.
701 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.
702 With cooperative suspend, there are no finish_async_suspend transitions since there's no path back from asyns_suspend requested to running.
703 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.
705 default:
706 mono_fatal_with_history ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
711 This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
713 Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
714 That state only works as long as the only managed state touched is blitable and was pinned before the transition.
716 It returns the action the caller must perform:
718 - Continue: Entered blocking state sucessfully;
719 - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
722 MonoDoBlockingResult
723 mono_threads_transition_do_blocking (MonoThreadInfo* info, const char *func)
725 int raw_state, cur_state, suspend_count;
726 gboolean no_safepoints;
728 retry_state_change:
729 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
730 switch (cur_state) {
732 case STATE_RUNNING: //transition to blocked
733 if (!(suspend_count == 0))
734 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
735 if (no_safepoints)
736 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in state RUNNING with DO_BLOCKING");
737 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count, no_safepoints), raw_state) != raw_state)
738 goto retry_state_change;
739 trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, no_safepoints, 0);
740 return DoBlockingContinue;
742 case STATE_ASYNC_SUSPEND_REQUESTED:
743 if (!(suspend_count > 0))
744 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
745 if (no_safepoints)
746 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in state ASYNC_SUSPEND_REQUESTED with DO_BLOCKING");
747 trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, no_safepoints, 0);
748 return DoBlockingPollAndRetry;
750 STATE_ASYNC_SUSPENDED
751 STATE_SELF_SUSPENDED: Code should not be running while suspended.
752 STATE_BLOCKING:
753 STATE_BLOCKING_SUSPEND_REQUESTED:
754 STATE_BLOCKING_SELF_SUSPENDED: Blocking is not nestabled
755 STATE_BLOCKING_ASYNC_SUSPENDED: Blocking is not nestable _and_ code should not be running while suspended
757 default:
758 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));
763 This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
764 until its resumed before continuing.
766 It returns one of:
767 -Ok: Done with blocking, just move on;
768 -Wait: This thread was suspended while in blocking, wait for resume.
770 MonoDoneBlockingResult
771 mono_threads_transition_done_blocking (MonoThreadInfo* info, const char *func)
773 int raw_state, cur_state, suspend_count;
774 gboolean no_safepoints;
776 retry_state_change:
777 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
778 switch (cur_state) {
779 case STATE_BLOCKING:
780 if (!(suspend_count == 0))
781 mono_fatal_with_history ("%s suspend_count = %d, but should be == 0", func, suspend_count);
782 if (no_safepoints)
783 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
784 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count, no_safepoints), raw_state) != raw_state)
785 goto retry_state_change;
786 trace_state_change_sigsafe ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, no_safepoints, 0, func);
787 return DoneBlockingOk;
788 case STATE_BLOCKING_SUSPEND_REQUESTED:
789 if (!(suspend_count > 0))
790 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
791 if (no_safepoints)
792 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
793 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SELF_SUSPENDED, suspend_count, no_safepoints), raw_state) != raw_state)
794 goto retry_state_change;
795 trace_state_change_with_func ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_SELF_SUSPENDED, no_safepoints, 0, func);
796 return DoneBlockingWait;
798 STATE_RUNNING: //Blocking was aborted and not properly restored
799 STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
800 STATE_ASYNC_SUSPENDED
801 STATE_SELF_SUSPENDED: Code should not be running while suspended.
802 STATE_BLOCKING_SELF_SUSPENDED: This an exit state of done blocking
803 STATE_BLOCKING_ASYNC_SUSPENDED: This is an exit state of done blocking
805 default:
806 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
811 Transition a thread in what should be a blocking state back to running state.
812 This is different that done blocking because the goal is to get back to blocking once we're done.
813 This is required to be able to bail out of blocking in case we're back to inside the runtime.
815 It returns one of:
816 -Ignore: Thread was not in blocking, nothing to do;
817 -IgnoreAndPoll: Thread was not blocking and there's a pending suspend that needs to be processed;
818 -Ok: Blocking state successfully aborted;
819 -Wait: Blocking state successfully aborted, there's a pending suspend to be processed though, wait for resume.
821 MonoAbortBlockingResult
822 mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info, const char *func)
824 int raw_state, cur_state, suspend_count;
825 gboolean no_safepoints;
827 retry_state_change:
828 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
829 switch (cur_state) {
830 case STATE_RUNNING: //thread already in runnable state
831 /* Even though we're going to ignore this transition, still
832 * assert about no_safepoints. Rationale: make it easier to catch
833 * cases where we would be in ASYNC_SUSPEND_REQUESTED with
834 * no_safepoints set, since those are polling points.
836 /* WISH: make this fatal. Unfortunately in that case, if a
837 * thread asserts somewhere because no_safepoints was set when it
838 * shouldn't have been, we get a second assertion here while
839 * unwinding. */
840 if (no_safepoints)
841 g_warning ("Warning: no_safepoints = TRUE, but should be FALSE in state RUNNING with ABORT_BLOCKING");
842 trace_state_change_sigsafe ("ABORT_BLOCKING", info, raw_state, cur_state, no_safepoints, 0, func);
843 return AbortBlockingIgnore;
845 case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
846 if (no_safepoints)
847 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in state ASYNC_SUSPEND_REQUESTED with ABORT_BLOCKING");
848 trace_state_change_sigsafe ("ABORT_BLOCKING", info, raw_state, cur_state, no_safepoints, 0, func);
849 return AbortBlockingIgnoreAndPoll;
851 case STATE_BLOCKING:
852 if (!(suspend_count == 0))
853 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
854 if (no_safepoints)
855 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
856 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count, FALSE), raw_state) != raw_state)
857 goto retry_state_change;
858 trace_state_change_sigsafe ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, FALSE, 0, func);
859 return AbortBlockingOk;
860 case STATE_BLOCKING_SUSPEND_REQUESTED:
861 if (!(suspend_count > 0))
862 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
863 if (no_safepoints)
864 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
865 if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SELF_SUSPENDED, suspend_count, FALSE), raw_state) != raw_state)
866 goto retry_state_change;
867 trace_state_change_with_func ("ABORT_BLOCKING", info, raw_state, STATE_BLOCKING_SELF_SUSPENDED, FALSE, 0, func);
868 return AbortBlockingWait;
870 STATE_ASYNC_SUSPENDED:
871 STATE_SELF_SUSPENDED: Code should not be running while suspended.
872 STATE_BLOCKING_SELF_SUSPENDED: This is an exit state of done blocking, can't happen here.
873 STATE_BLOCKING_ASYNC_SUSPENDED: This is an exit state of abort blocking, can't happen here.
875 default:
876 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
881 Set the no_safepoints flag on an executing GC Unsafe thread.
882 The no_safepoints bit prevents polling (hence self-suspending) and transitioning from GC Unsafe to GC Safe.
883 Thus the thread will not be (cooperatively) interrupted while the bit is set.
885 We don't allow nesting no_safepoints regions, so the flag must be initially unset.
887 Since a suspend initiator may at any time request that a thread should suspend,
888 ASYNC_SUSPEND_REQUESTED is allowed to have the no_safepoints bit set, too.
889 (Future: We could augment this function to return a return value that tells the
890 thread to poll and retry the transition since if we enter here in the
891 ASYNC_SUSPEND_REQUESTED state).
893 void
894 mono_threads_transition_begin_no_safepoints (MonoThreadInfo *info, const char *func)
896 int raw_state, cur_state, suspend_count;
897 gboolean no_safepoints;
899 retry_state_change:
900 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
901 switch (cur_state) {
902 case STATE_RUNNING:
903 case STATE_ASYNC_SUSPEND_REQUESTED:
904 /* Maybe revisit this. But for now, don't allow nesting. */
905 if (no_safepoints)
906 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE with BEGIN_NO_SAFEPOINTS. Can't nest no safepointing regions");
907 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count, TRUE), raw_state) != raw_state)
908 goto retry_state_change;
909 trace_state_change_with_func ("BEGIN_NO_SAFEPOINTS", info, raw_state, cur_state, TRUE, 0, func);
910 return;
912 STATE_STARTING:
913 STATE_DETACHED:
914 STATE_SELF_SUSPENDED:
915 STATE_ASYNC_SUSPENDED:
916 STATE_BLOCKING:
917 STATE_BLOCKING_ASYNC_SUSPENDED:
918 STATE_BLOCKING_SELF_SUSPENDED:
919 STATE_BLOCKING_SUSPEND_REQUESTED:
920 no_safepoints only allowed for threads that are executing and GC Unsafe.
922 default:
923 mono_fatal_with_history ("Cannot transition thread %p from %s with BEGIN_NO_SAFEPOINTS", mono_thread_info_get_tid (info), state_name (cur_state));
928 Unset the no_safepoints flag on an executing GC Unsafe thread.
929 The no_safepoints bit prevents polling (hence self-suspending) and transitioning from GC Unsafe to GC Safe.
930 Thus the thread will not be (cooperatively) interrupted while the bit is set.
932 We don't allow nesting no_safepoints regions, so the flag must be initially set.
934 Since a suspend initiator may at any time request that a thread should suspend,
935 ASYNC_SUSPEND_REQUESTED is allowed to have the no_safepoints bit set, too.
936 (Future: We could augment this function to perform the transition and then
937 return a return value that tells the thread to poll (and safepoint) if we enter
938 here in the ASYNC_SUSPEND_REQUESTED state).
940 void
941 mono_threads_transition_end_no_safepoints (MonoThreadInfo *info, const char *func)
943 int raw_state, cur_state, suspend_count;
944 gboolean no_safepoints;
946 retry_state_change:
947 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
948 switch (cur_state) {
949 case STATE_RUNNING:
950 case STATE_ASYNC_SUSPEND_REQUESTED:
951 if (!no_safepoints)
952 mono_fatal_with_history ("no_safepoints = FALSE, but should be TRUE with END_NO_SAFEPOINTS. Unbalanced no safepointing region");
953 if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count, FALSE), raw_state) != raw_state)
954 goto retry_state_change;
955 trace_state_change_with_func ("END_NO_SAFEPOINTS", info, raw_state, cur_state, TRUE, 0, func);
956 return;
958 STATE_STARTING:
959 STATE_DETACHED:
960 STATE_SELF_SUSPENDED:
961 STATE_ASYNC_SUSPENDED:
962 STATE_BLOCKING:
963 STATE_BLOCKING_ASYNC_SUSPENDED:
964 STATE_BLOCKING_SELF_SUSPENDED:
965 STATE_BLOCKING_SUSPEND_REQUESTED:
966 no_safepoints only allowed for threads that are executing and GC Unsafe.
968 default:
969 mono_fatal_with_history ("Cannot transition thread %p from %s with END_NO_SAFEPOINTS", mono_thread_info_get_tid (info), state_name (cur_state));
973 // State checking code
975 * Return TRUE is the thread is in a runnable state.
977 gboolean
978 mono_thread_info_is_running (MonoThreadInfo *info)
980 switch (mono_thread_info_current_state (info)) {
981 case STATE_RUNNING:
982 case STATE_ASYNC_SUSPEND_REQUESTED:
983 case STATE_BLOCKING_SUSPEND_REQUESTED:
984 case STATE_BLOCKING:
985 return TRUE;
987 return FALSE;
991 * Return TRUE is the thread is in an usable (suspendable) state
993 gboolean
994 mono_thread_info_is_live (MonoThreadInfo *info)
996 switch (mono_thread_info_current_state (info)) {
997 case STATE_STARTING:
998 case STATE_DETACHED:
999 return FALSE;
1001 return TRUE;
1005 mono_thread_info_suspend_count (MonoThreadInfo *info)
1007 return info->thread_state.suspend_count;
1011 mono_thread_info_current_state (MonoThreadInfo *info)
1013 return info->thread_state.state;
1016 const char*
1017 mono_thread_state_name (int state)
1019 return state_name (state);
1022 gboolean
1023 mono_thread_is_gc_unsafe_mode (void)
1025 MonoThreadInfo *cur = mono_thread_info_current ();
1027 if (!cur)
1028 return FALSE;
1030 switch (mono_thread_info_current_state (cur)) {
1031 case STATE_RUNNING:
1032 case STATE_ASYNC_SUSPEND_REQUESTED:
1033 return TRUE;
1034 default:
1035 return FALSE;
1039 gboolean
1040 mono_thread_info_will_not_safepoint (MonoThreadInfo *info)
1042 return info->thread_state.no_safepoints;