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>
17 /*thread state helpers*/
19 get_thread_state (int thread_state
)
21 MonoThreadStateMachine state
;
22 state
.raw
= thread_state
;
27 get_thread_suspend_count (int thread_state
)
29 MonoThreadStateMachine state
;
30 state
.raw
= thread_state
;
31 return state
.suspend_count
;
35 get_thread_no_safepoints (int thread_state
)
37 MonoThreadStateMachine state
;
38 state
.raw
= thread_state
;
39 return state
.no_safepoints
;
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.
54 state
.state
= thread_state
;
55 state
.no_safepoints
= no_safepoints
;
56 state
.suspend_count
= suspend_count
;
61 thread_state_cas (MonoThreadStateMachine
*state
, int new_raw
, int old_raw
)
63 return mono_atomic_cas_i32 (&state
->raw
, new_raw
, old_raw
);
67 state_name (int state
)
69 static const char *state_names
[] = {
76 "ASYNC_SUSPEND_REQUESTED",
79 "STATE_BLOCKING_ASYNC_SUSPENDED",
80 "STATE_BLOCKING_SELF_SUSPENDED",
81 "STATE_BLOCKING_SUSPEND_REQUESTED",
83 return state_names
[get_thread_state (state
)];
87 unwrap_thread_state (MonoThreadInfo
* info
,
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
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))
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
);
115 g_assert (!no_safepoints
);
118 g_assert (suspend_count
== 0);
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
);
127 case STATE_ASYNC_SUSPEND_REQUESTED
:
128 g_assert (suspend_count
> 0);
131 g_assert (!no_safepoints
);
132 g_assert (suspend_count
== 0);
135 g_error ("Invalid state %d", cur_state
);
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",
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
,
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
);
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",
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
,
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
);
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.
187 mono_threads_transition_attach (MonoThreadInfo
* info
)
189 int raw_state
, cur_state
, suspend_count
;
190 gboolean no_safepoints
;
193 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
196 if (!(suspend_count
== 0))
197 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count
);
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);
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.
217 mono_threads_transition_detach (MonoThreadInfo
*info
)
219 int raw_state
, cur_state
, suspend_count
;
220 gboolean no_safepoints
;
223 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
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
);
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);
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
:
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
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),
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 ());
270 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
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
:
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
295 if (!(suspend_count
== 0))
296 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count
);
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
);
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.
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.
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
);
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
);
358 mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
359 if (cur_state
== STATE_BLOCKING_SUSPEND_REQUESTED
)
360 return TRUE
; /*ReqPeekBlockingSuspendRequestedBlockingSuspendRequested;*/
362 return FALSE
; /*ReqPeekBlockingSuspendRequestedBlockingSuspended;*/
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
369 Can't happen - should've had a suspension request in the first phase.
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.
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
));
396 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
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
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.
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).
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.
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]
462 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
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
);
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);
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
);
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.
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
);
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.
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
:
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.
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
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.
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.
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]
567 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
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
);
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
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.
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.
613 mono_threads_transition_abort_async_suspend (MonoThreadInfo
* info
)
615 int raw_state
, cur_state
, suspend_count
;
616 gboolean no_safepoints
;
619 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
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
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
);
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);
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.
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.
662 mono_threads_transition_finish_async_suspend (MonoThreadInfo
* info
)
664 int raw_state
, cur_state
, suspend_count
;
665 gboolean no_safepoints
;
668 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
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
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 */
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
);
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.
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;
723 mono_threads_transition_do_blocking (MonoThreadInfo
* info
, const char *func
)
725 int raw_state
, cur_state
, suspend_count
;
726 gboolean no_safepoints
;
729 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
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
);
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
);
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.
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
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.
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
;
777 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
780 if (!(suspend_count
== 0))
781 mono_fatal_with_history ("%s suspend_count = %d, but should be == 0", func
, suspend_count
);
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
);
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
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.
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
;
828 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
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
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
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
;
852 if (!(suspend_count
== 0))
853 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count
);
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
);
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.
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).
894 mono_threads_transition_begin_no_safepoints (MonoThreadInfo
*info
, const char *func
)
896 int raw_state
, cur_state
, suspend_count
;
897 gboolean no_safepoints
;
900 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
903 case STATE_ASYNC_SUSPEND_REQUESTED
:
904 /* Maybe revisit this. But for now, don't allow nesting. */
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
);
914 STATE_SELF_SUSPENDED:
915 STATE_ASYNC_SUSPENDED:
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.
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).
941 mono_threads_transition_end_no_safepoints (MonoThreadInfo
*info
, const char *func
)
943 int raw_state
, cur_state
, suspend_count
;
944 gboolean no_safepoints
;
947 UNWRAP_THREAD_STATE (raw_state
, cur_state
, suspend_count
, no_safepoints
, info
);
950 case STATE_ASYNC_SUSPEND_REQUESTED
:
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
);
960 STATE_SELF_SUSPENDED:
961 STATE_ASYNC_SUSPENDED:
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.
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.
978 mono_thread_info_is_running (MonoThreadInfo
*info
)
980 switch (mono_thread_info_current_state (info
)) {
982 case STATE_ASYNC_SUSPEND_REQUESTED
:
983 case STATE_BLOCKING_SUSPEND_REQUESTED
:
991 * Return TRUE is the thread is in an usable (suspendable) state
994 mono_thread_info_is_live (MonoThreadInfo
*info
)
996 switch (mono_thread_info_current_state (info
)) {
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
;
1017 mono_thread_state_name (int state
)
1019 return state_name (state
);
1023 mono_thread_is_gc_unsafe_mode (void)
1025 MonoThreadInfo
*cur
= mono_thread_info_current ();
1030 switch (mono_thread_info_current_state (cur
)) {
1032 case STATE_ASYNC_SUSPEND_REQUESTED
:
1040 mono_thread_info_will_not_safepoint (MonoThreadInfo
*info
)
1042 return info
->thread_state
.no_safepoints
;