[threadpool] Saner default for max number of worker threads on android and ios
[mono-project.git] / mono / utils / mono-threads-state-machine.c
blob098e02c7ee32e1a558cd6c103ac6a8a03e70a486
1 #include <config.h>
3 #include <mono/utils/mono-compiler.h>
4 #include <mono/utils/mono-threads.h>
5 #include <mono/utils/mono-tls.h>
6 #include <mono/utils/mono-memory-model.h>
7 #include <mono/utils/atomic.h>
8 #include <mono/utils/checked-build.h>
10 #include <errno.h>
12 /*thread state helpers*/
13 static inline int
14 get_thread_state (int thread_state)
16 return thread_state & THREAD_STATE_MASK;
19 static inline int
20 get_thread_suspend_count (int thread_state)
22 return (thread_state & THREAD_SUSPEND_COUNT_MASK) >> THREAD_SUSPEND_COUNT_SHIFT;
25 static inline int
26 build_thread_state (int thread_state, int suspend_count)
28 g_assert (suspend_count >= 0 && suspend_count <= THREAD_SUSPEND_COUNT_MAX);
29 g_assert (thread_state >= 0 && thread_state <= STATE_MAX);
31 return thread_state | (suspend_count << THREAD_SUSPEND_COUNT_SHIFT);
34 static const char*
35 state_name (int state)
37 static const char *state_names [] = {
38 "STARTING",
39 "RUNNING",
40 "DETACHED",
41 "ASYNC_SUSPENDED",
42 "SELF_SUSPENDED",
43 "ASYNC_SUSPEND_REQUESTED",
44 "SELF_SUSPEND_REQUESTED",
45 "STATE_BLOCKING",
46 "STATE_BLOCKING_AND_SUSPENDED",
48 return state_names [get_thread_state (state)];
51 #define UNWRAP_THREAD_STATE(RAW,CUR,COUNT,INFO) do { \
52 RAW = (INFO)->thread_state; \
53 CUR = get_thread_state (RAW); \
54 COUNT = get_thread_suspend_count (RAW); \
55 } while (0)
57 static void
58 check_thread_state (MonoThreadInfo* info)
60 int raw_state, cur_state, suspend_count;
61 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
62 switch (cur_state) {
63 case STATE_STARTING:
64 case STATE_RUNNING:
65 case STATE_DETACHED:
66 g_assert (suspend_count == 0);
67 break;
68 case STATE_ASYNC_SUSPENDED:
69 case STATE_SELF_SUSPENDED:
70 case STATE_ASYNC_SUSPEND_REQUESTED:
71 case STATE_SELF_SUSPEND_REQUESTED:
72 case STATE_BLOCKING_AND_SUSPENDED:
73 g_assert (suspend_count > 0);
74 break;
75 case STATE_BLOCKING: //this is a special state that can have zero or positive suspend count.
76 break;
77 default:
78 g_error ("Invalid state %d", cur_state);
82 static inline void
83 trace_state_change (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, int suspend_count_delta)
85 check_thread_state (info);
86 THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s -> %s (%d -> %d)\n",
87 transition,
88 mono_thread_info_get_tid (info),
89 state_name (get_thread_state (cur_raw_state)),
90 state_name (next_state),
91 get_thread_suspend_count (cur_raw_state),
92 get_thread_suspend_count (cur_raw_state) + suspend_count_delta);
94 CHECKED_BUILD_THREAD_TRANSITION (transition, info, get_thread_state (cur_raw_state), get_thread_suspend_count (cur_raw_state), next_state, suspend_count_delta);
98 This is the transition that signals that a thread is functioning.
99 Its main goal is to catch threads been witnessed before been fully registered.
101 void
102 mono_threads_transition_attach (MonoThreadInfo* info)
104 int raw_state, cur_state, suspend_count;
106 retry_state_change:
107 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
108 switch (cur_state) {
109 case STATE_STARTING:
110 g_assert (suspend_count == 0);
111 if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
112 goto retry_state_change;
113 trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, 0);
114 break;
115 default:
116 g_error ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
121 This is the transition that signals that a thread is no longer registered with the runtime.
122 Its main goal is to catch threads been witnessed after they detach.
124 This returns TRUE is the transition succeeded.
125 If it returns false it means that there's a pending suspend that should be acted upon.
127 gboolean
128 mono_threads_transition_detach (MonoThreadInfo *info)
130 int raw_state, cur_state, suspend_count;
132 retry_state_change:
133 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
134 switch (cur_state) {
135 case STATE_RUNNING:
136 g_assert (suspend_count == 0);
137 if (InterlockedCompareExchange (&info->thread_state, STATE_DETACHED, raw_state) != raw_state)
138 goto retry_state_change;
139 trace_state_change ("DETACH", info, raw_state, STATE_DETACHED, 0);
140 return TRUE;
141 case STATE_ASYNC_SUSPEND_REQUESTED: //Can't detach until whoever asked us to suspend to be happy with us
142 return FALSE;
144 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
145 STATE_SELF_SUSPENDED: Code should not be running while suspended.
146 STATE_SELF_SUSPEND_REQUESTED: This is a bug in the self suspend code that didn't execute the second part of it
147 STATE_BLOCKING: This is a bug in the coop code that forgot to do a finish blocking before exiting.
148 STATE_BLOCKING_AND_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
150 default:
151 g_error ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
156 This transition initiates the suspension of the current thread.
158 void
159 mono_threads_transition_request_self_suspension (MonoThreadInfo *info)
161 int raw_state, cur_state, suspend_count;
162 g_assert (info == mono_thread_info_current ());
164 retry_state_change:
165 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
167 switch (cur_state) {
168 case STATE_RUNNING: //Post a self suspend request
169 g_assert (suspend_count == 0);
170 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
171 goto retry_state_change;
172 trace_state_change ("SELF_SUSPEND_REQUEST", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 1);
173 break;
175 case STATE_ASYNC_SUSPEND_REQUESTED: //Bump the suspend count but don't change the request type as async takes preference
176 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
177 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
178 goto retry_state_change;
179 trace_state_change ("SUSPEND_REQUEST", info, raw_state, cur_state, 1);
180 break;
182 Other states:
183 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
184 STATE_SELF_SUSPENDED: Code should not be running while suspended.
185 STATE_SELF_SUSPEND_REQUESTED: Self suspends should not nest as begin/end should be paired. [1]
186 STATE_BLOCKING:
187 STATE_BLOCKING_AND_SUSPENDED: Self suspension cannot be started when the thread is in blocking state as it must finish first
189 [1] This won't trap this sequence of requests: self suspend, async suspend and self suspend.
190 If this turns to be an issue we can introduce a new suspend request state for when both have been requested.
192 default:
193 g_error ("Cannot transition thread %p from %s with SUSPEND_REQUEST", mono_thread_info_get_tid (info), state_name (cur_state));
198 This transition initiates the suspension of another thread.
200 Returns one of the following values:
202 - AsyncSuspendInitSuspend: Thread suspend requested, async suspend needs to be done.
203 - AsyncSuspendAlreadySuspended: Thread already suspended, nothing to do.
204 - AsyncSuspendWait: Self suspend in progress, asked it to notify us. Caller must add target to the notification set.
206 MonoRequestAsyncSuspendResult
207 mono_threads_transition_request_async_suspension (MonoThreadInfo *info)
209 int raw_state, cur_state, suspend_count;
210 g_assert (info != mono_thread_info_current ());
212 retry_state_change:
213 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
215 switch (cur_state) {
216 case STATE_RUNNING: //Post an async suspend request
217 g_assert (suspend_count == 0);
218 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
219 goto retry_state_change;
220 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
221 return AsyncSuspendInitSuspend; //This is the first async suspend request against the target
223 case STATE_ASYNC_SUSPENDED:
224 case STATE_SELF_SUSPENDED: //Async suspend can suspend the same thread multiple times as it starts from the outside
225 case STATE_BLOCKING_AND_SUSPENDED:
226 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
227 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
228 goto retry_state_change;
229 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
230 return AsyncSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
232 case STATE_SELF_SUSPEND_REQUESTED: //This suspend needs to notify the initiator, so we need to promote the suspend to async
233 g_assert (suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX);
234 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, suspend_count + 1), raw_state) != raw_state)
235 goto retry_state_change;
236 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
237 return AsyncSuspendWait; //This is the first async suspend request, change the thread and let it notify us [1]
239 case STATE_BLOCKING:
240 g_assert (suspend_count < THREAD_SUSPEND_COUNT_MAX);
241 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
242 goto retry_state_change;
243 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
244 return AsyncSuspendAlreadySuspended; //A thread in the blocking state has its state saved so we can treat it as suspended.
248 [1] It's questionable on what to do if we hit the beginning of a self suspend.
249 The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
251 STATE_ASYNC_SUSPEND_REQUESTED: Since there can only be one async suspend in progress and it must finish, it should not be possible to witness this.
253 default:
254 g_error ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
256 return (MonoRequestAsyncSuspendResult) FALSE;
260 Check the current state of the thread and try to init a self suspend.
261 This must be called with self state saved.
263 Returns one of the following values:
265 - Resumed: Async resume happened and current thread should keep running
266 - Suspend: Caller should wait for a resume signal
267 - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
268 suspend should start.
271 MonoSelfSupendResult
272 mono_threads_transition_state_poll (MonoThreadInfo *info)
274 int raw_state, cur_state, suspend_count;
275 g_assert (info == mono_thread_info_current ());
277 retry_state_change:
278 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
279 switch (cur_state) {
280 case STATE_RUNNING:
281 g_assert (suspend_count == 0);
282 trace_state_change ("STATE_POLL", info, raw_state, cur_state, 0);
283 return SelfSuspendResumed; //We're fine, don't suspend
285 case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
286 case STATE_SELF_SUSPEND_REQUESTED: //Start the self suspend process
287 g_assert (suspend_count > 0);
288 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
289 goto retry_state_change;
290 trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, 0);
291 if (cur_state == STATE_SELF_SUSPEND_REQUESTED)
292 return SelfSuspendWait; //Caller should wait for resume
293 else
294 return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
297 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
298 STATE_SELF_SUSPENDED: Code should not be running while suspended.
299 STATE_BLOCKING:
300 STATE_BLOCKING_AND_SUSPENDED: Pool is a local state transition. No VM activities are allowed while in blocking mode.
302 default:
303 g_error ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
308 Try to resume a suspended thread.
310 Returns one of the following values:
311 - Sucess: The thread was resumed.
312 - Error: The thread was not suspended in the first place. [2]
313 - InitSelfResume: The thread is blocked on self suspend and should be resumed
314 - InitAsycResume: The thread is blocked on async suspend and should be resumed
315 - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
317 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
318 used as a suspend permit and cancel each other.
320 Suspend permits are really useful to implement managed synchronization structures that
321 don't consume native resources. The downside is that they further complicate the design of this
322 system as the RUNNING state now has a non zero suspend counter.
324 It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
326 One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
327 This would make permits really harder to add.
329 MonoResumeResult
330 mono_threads_transition_request_resume (MonoThreadInfo* info)
332 int raw_state, cur_state, suspend_count;
333 g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
335 retry_state_change:
336 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
337 switch (cur_state) {
338 case STATE_RUNNING: //Thread already running.
339 g_assert (suspend_count == 0);
340 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
341 return ResumeError; //Resume failed because thread was not blocked
343 case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
344 if (suspend_count == 0) {
345 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
346 return ResumeError;
347 } else {
348 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
349 goto retry_state_change;
350 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
351 return ResumeOk; //Resume worked and there's nothing for the caller to do.
353 break;
354 case STATE_ASYNC_SUSPENDED:
355 case STATE_SELF_SUSPENDED:
356 case STATE_BLOCKING_AND_SUSPENDED: //Decrease the suspend_count and maybe resume
357 g_assert (suspend_count > 0);
358 if (suspend_count > 1) {
359 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
360 goto retry_state_change;
361 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
363 return ResumeOk; //Resume worked and there's nothing for the caller to do.
364 } else {
365 if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
366 goto retry_state_change;
367 trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
369 if (cur_state == STATE_ASYNC_SUSPENDED)
370 return ResumeInitAsyncResume; //Resume worked and caller must do async resume
371 else if (cur_state == STATE_SELF_SUSPENDED)
372 return ResumeInitSelfResume; //Resume worked and caller must do self resume
373 else
374 return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
377 case STATE_SELF_SUSPEND_REQUESTED: //Self suspend was requested but another thread decided to resume it.
378 g_assert (suspend_count > 0);
379 if (suspend_count > 1) {
380 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
381 goto retry_state_change;
382 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
383 } else {
384 if (InterlockedCompareExchange (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
385 goto retry_state_change;
386 trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
388 return ResumeOk; //Resume worked and there's nothing for the caller to do (the target never actually suspend).
391 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
393 [3] A self-resume makes no sense given it requires the thread to be running, which means its suspend count must be zero. A self resume would make
394 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
396 [4] It's questionable on whether a resume (an async operation) should be able to cancel a self suspend. The scenario where this would happen
397 is similar to the one described in [2] when this is used for as a synchronization primitive.
399 If this turns to be a problem we should either implement [2] or make this an invalid transition.
402 default:
403 g_error ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
408 This performs the last step of async suspend.
410 Returns TRUE if the caller should wait for resume.
412 gboolean
413 mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
415 int raw_state, cur_state, suspend_count;
417 retry_state_change:
418 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
419 switch (cur_state) {
421 case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
422 case STATE_BLOCKING_AND_SUSPENDED: //async suspend raced with blocking and lost
423 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, 0);
424 return FALSE; //let self suspend wait
426 case STATE_ASYNC_SUSPEND_REQUESTED:
427 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count), raw_state) != raw_state)
428 goto retry_state_change;
429 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, 0);
430 return TRUE; //Async suspend worked, now wait for resume
433 STATE_RUNNING: A thread cannot escape suspension once requested.
434 STATE_ASYNC_SUSPENDED: There can be only one suspend initiator at a given time, meaning this state should have been visible on the first stage of suspend.
435 STATE_SELF_SUSPEND_REQUESTED: When self suspend and async suspend happen together, they converge to async suspend so this state should not be visible.
436 STATE_BLOCKING: Async suspend only begins if a transition to async suspend requested happened. Blocking would have put us into blocking with positive suspend count if it raced with async finish.
438 default:
439 g_error ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
444 This the compensatory transition for failed async suspend.
446 Async suspend can land on a thread as it began cleaning up and is no longer
447 functional. This happens as cleanup is a racy process from the async suspend
448 perspective. The thread could have cleaned up its domain or jit_tls, for example.
450 It can only transition the state as left by a sucessfull finish async suspend transition.
453 void
454 mono_threads_transition_async_suspend_compensation (MonoThreadInfo* info)
456 int raw_state, cur_state, suspend_count;
458 retry_state_change:
459 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
460 switch (cur_state) {
462 case STATE_ASYNC_SUSPENDED:
464 Must be one since if a self suspend is in progress the thread should still be async suspendable.
465 If count > 1 and no self suspend is in progress then it means one of the following two.
466 - the thread was previously suspended, which means we should never reach end suspend in the first place.
467 - another suspend happened concurrently, which means the global suspend lock didn't happen.
469 g_assert (suspend_count == 1);
470 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count - 1), raw_state) != raw_state)
471 goto retry_state_change;
472 trace_state_change ("COMPENSATE_FINISH_ASYNC_SUSPEND", info, raw_state, STATE_RUNNING, -1);
473 break;
475 STATE_RUNNING
476 STATE_SELF_SUSPENDED
477 STATE_ASYNC_SUSPEND_REQUESTED
478 STATE_BLOCKING
479 STATE_BLOCKING_AND_SUSPENDED
480 STATE_SELF_SUSPEND_REQUESTED: All those are invalid end states of a sucessfull finish async suspend
482 default:
483 g_error ("Cannot transition thread %p from %s with COMPENSATE_FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
489 This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
491 Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
492 That state only works as long as the only managed state touched is blitable and was pinned before the transition.
494 It returns the action the caller must perform:
496 - Continue: Entered blocking state sucessfully;
497 - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
500 MonoDoBlockingResult
501 mono_threads_transition_do_blocking (MonoThreadInfo* info)
503 int raw_state, cur_state, suspend_count;
505 retry_state_change:
506 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
507 switch (cur_state) {
509 case STATE_RUNNING: //transition to blocked
510 g_assert (suspend_count == 0);
511 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count), raw_state) != raw_state)
512 goto retry_state_change;
513 trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, 0);
514 return DoBlockingContinue;
516 case STATE_ASYNC_SUSPEND_REQUESTED:
517 g_assert (suspend_count > 0);
518 trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, 0);
519 return DoBlockingPollAndRetry;
521 STATE_ASYNC_SUSPENDED
522 STATE_SELF_SUSPENDED: Code should not be running while suspended.
523 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
524 STATE_BLOCKING:
525 STATE_BLOCKING_AND_SUSPENDED: Blocking is not nestabled
527 default:
528 g_error ("Cannot transition thread %p from %s with DO_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
533 This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
534 until its resumed before continuing.
536 It returns one of:
537 -Aborted: The blocking operation was aborted and not properly restored. Aborts can happen due to lazy loading and some n2m transitions;
538 -Ok: Done with blocking, just move on;
539 -Wait: This thread was async suspended, wait for resume
542 MonoDoneBlockingResult
543 mono_threads_transition_done_blocking (MonoThreadInfo* info)
545 int raw_state, cur_state, suspend_count;
547 retry_state_change:
548 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
549 switch (cur_state) {
550 case STATE_RUNNING: //Blocking was aborted and not properly restored
551 case STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
552 trace_state_change ("DONE_BLOCKING", info, raw_state, cur_state, 0);
553 return DoneBlockingAborted;
555 case STATE_BLOCKING:
556 if (suspend_count == 0) {
557 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
558 goto retry_state_change;
559 trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, 0);
560 return DoneBlockingOk;
561 } else {
562 g_assert (suspend_count >= 0);
563 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_BLOCKING_AND_SUSPENDED, suspend_count), raw_state) != raw_state)
564 goto retry_state_change;
565 trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_AND_SUSPENDED, 0);
566 return DoneBlockingWait;
570 STATE_ASYNC_SUSPENDED
571 STATE_SELF_SUSPENDED: Code should not be running while suspended.
572 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
573 STATE_BLOCKING_AND_SUSPENDED: This an exit state of done blocking
575 default:
576 g_error ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
581 Transition a thread in what should be a blocking state back to running state.
582 This is different that done blocking because the goal is to get back to blocking once we're done.
583 This is required to be able to bail out of blocking in case we're back to inside the runtime.
585 It returns one of:
586 -Ignore: Thread was not in blocking, nothing to do;
587 -IgnoreAndPool: Thread was not blocking and there's a pending suspend that needs to be processed;
588 -Ok: Blocking state successfully aborted;
589 -OkAndPool: Blocking state successfully aborted, there's a pending suspend to be processed though
591 MonoAbortBlockingResult
592 mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info)
594 int raw_state, cur_state, suspend_count;
596 retry_state_change:
597 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
598 switch (cur_state) {
599 case STATE_RUNNING: //thread already in runnable state
600 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
601 return AbortBlockingIgnore;
603 case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
604 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
605 return AbortBlockingIgnoreAndPoll;
607 case STATE_BLOCKING:
608 if (suspend_count == 0) {
609 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
610 goto retry_state_change;
611 trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, 0);
612 return AbortBlockingOk;
613 } else {
614 if (InterlockedCompareExchange (&info->thread_state, build_thread_state (STATE_SELF_SUSPEND_REQUESTED, suspend_count), raw_state) != raw_state)
615 goto retry_state_change;
616 trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_SELF_SUSPEND_REQUESTED, 0);
617 return AbortBlockingOkAndPool;
620 STATE_ASYNC_SUSPENDED:
621 STATE_SELF_SUSPENDED: Code should not be running while suspended.
622 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend.
623 STATE_BLOCKING_AND_SUSPENDED: This is an exit state of done blocking, can't happen here.
625 default:
626 g_error ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
630 MonoThreadUnwindState*
631 mono_thread_info_get_suspend_state (MonoThreadInfo *info)
633 int raw_state, cur_state, suspend_count;
634 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
635 switch (cur_state) {
636 case STATE_ASYNC_SUSPENDED:
637 return &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX];
638 case STATE_SELF_SUSPENDED:
639 case STATE_BLOCKING_AND_SUSPENDED:
640 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
641 case STATE_BLOCKING:
642 if (suspend_count > 0)
643 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
644 default:
646 STATE_RUNNING
647 STATE_SELF_SUSPENDED
648 STATE_ASYNC_SUSPEND_REQUESTED
649 STATE_BLOCKING: All those are invalid suspend states.
651 g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info), state_name (cur_state));
655 // State checking code
657 * Return TRUE is the thread is in a runnable state.
659 gboolean
660 mono_thread_info_is_running (MonoThreadInfo *info)
662 switch (get_thread_state (info->thread_state)) {
663 case STATE_RUNNING:
664 case STATE_ASYNC_SUSPEND_REQUESTED:
665 case STATE_SELF_SUSPEND_REQUESTED:
666 case STATE_BLOCKING:
667 return TRUE;
669 return FALSE;
673 * Return TRUE is the thread is in an usable (suspendable) state
675 gboolean
676 mono_thread_info_is_live (MonoThreadInfo *info)
678 switch (get_thread_state (info->thread_state)) {
679 case STATE_STARTING:
680 case STATE_DETACHED:
681 return FALSE;
683 return TRUE;
687 mono_thread_info_suspend_count (MonoThreadInfo *info)
689 return get_thread_suspend_count (info->thread_state);
693 mono_thread_info_current_state (MonoThreadInfo *info)
695 return get_thread_state (info->thread_state);
698 const char*
699 mono_thread_state_name (int state)
701 return state_name (state);