1 /**
2 * \file
3 */
4
5 #include <config.h>
6
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>
14
15 #include <errno.h>
16
17 /*thread state helpers*/
18 static inline int
get_thread_state(int thread_state)19 get_thread_state (int thread_state)
20 {
21 return thread_state & THREAD_STATE_MASK;
22 }
23
24 static inline int
get_thread_suspend_count(int thread_state)25 get_thread_suspend_count (int thread_state)
26 {
27 return (thread_state & THREAD_SUSPEND_COUNT_MASK) >> THREAD_SUSPEND_COUNT_SHIFT;
28 }
29
30 static inline int
build_thread_state(int thread_state,int suspend_count)31 build_thread_state (int thread_state, int suspend_count)
32 {
33 g_assert (suspend_count >= 0 && suspend_count <= THREAD_SUSPEND_COUNT_MAX);
34 g_assert (thread_state >= 0 && thread_state <= STATE_MAX);
35
36 return thread_state | (suspend_count << THREAD_SUSPEND_COUNT_SHIFT);
37 }
38
39 static const char*
state_name(int state)40 state_name (int state)
41 {
42 static const char *state_names [] = {
43 "STARTING",
44 "RUNNING",
45 "DETACHED",
46 "ASYNC_SUSPENDED",
47 "SELF_SUSPENDED",
48 "ASYNC_SUSPEND_REQUESTED",
49 "SELF_SUSPEND_REQUESTED",
50 "STATE_BLOCKING",
51 "STATE_BLOCKING_AND_SUSPENDED",
52 };
53 return state_names [get_thread_state (state)];
54 }
55
56 #define UNWRAP_THREAD_STATE(RAW,CUR,COUNT,INFO) do { \
57 RAW = (INFO)->thread_state; \
58 CUR = get_thread_state (RAW); \
59 COUNT = get_thread_suspend_count (RAW); \
60 } while (0)
61
62 static void
check_thread_state(MonoThreadInfo * info)63 check_thread_state (MonoThreadInfo* info)
64 {
65 int raw_state, cur_state, suspend_count;
66 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
67 switch (cur_state) {
68 case STATE_STARTING:
69 case STATE_RUNNING:
70 case STATE_DETACHED:
71 g_assert (suspend_count == 0);
72 break;
73 case STATE_ASYNC_SUSPENDED:
74 case STATE_SELF_SUSPENDED:
75 case STATE_ASYNC_SUSPEND_REQUESTED:
76 case STATE_SELF_SUSPEND_REQUESTED:
77 case STATE_BLOCKING_AND_SUSPENDED:
78 g_assert (suspend_count > 0);
79 break;
80 case STATE_BLOCKING: //this is a special state that can have zero or positive suspend count.
81 break;
82 default:
83 g_error ("Invalid state %d", cur_state);
84 }
85 }
86
87 static inline void
trace_state_change(const char * transition,MonoThreadInfo * info,int cur_raw_state,int next_state,int suspend_count_delta)88 trace_state_change (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, int suspend_count_delta)
89 {
90 check_thread_state (info);
91 THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s -> %s (%d -> %d)\n",
92 transition,
93 mono_thread_info_get_tid (info),
94 state_name (get_thread_state (cur_raw_state)),
95 state_name (next_state),
96 get_thread_suspend_count (cur_raw_state),
97 get_thread_suspend_count (cur_raw_state) + suspend_count_delta);
98
99 CHECKED_BUILD_THREAD_TRANSITION (transition, info, get_thread_state (cur_raw_state), get_thread_suspend_count (cur_raw_state), next_state, suspend_count_delta);
100 }
101
102 /*
103 This is the transition that signals that a thread is functioning.
104 Its main goal is to catch threads been witnessed before been fully registered.
105 */
106 void
mono_threads_transition_attach(MonoThreadInfo * info)107 mono_threads_transition_attach (MonoThreadInfo* info)
108 {
109 int raw_state, cur_state, suspend_count;
110
111 retry_state_change:
112 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
113 switch (cur_state) {
114 case STATE_STARTING:
115 if (!(suspend_count == 0))
116 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
117 if (mono_atomic_cas_i32 (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
118 goto retry_state_change;
119 trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, 0);
120 break;
121 default:
122 mono_fatal_with_history ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
123 }
124 }
125
126 /*
127 This is the transition that signals that a thread is no longer registered with the runtime.
128 Its main goal is to catch threads been witnessed after they detach.
129
130 This returns TRUE is the transition succeeded.
131 If it returns false it means that there's a pending suspend that should be acted upon.
132 */
133 gboolean
mono_threads_transition_detach(MonoThreadInfo * info)134 mono_threads_transition_detach (MonoThreadInfo *info)
135 {
136 int raw_state, cur_state, suspend_count;
137
138 retry_state_change:
139 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
140 switch (cur_state) {
141 case STATE_RUNNING:
142 case STATE_BLOCKING: /* An OS thread on coop goes STARTING->BLOCKING->RUNNING->BLOCKING->DETACHED */
143 if (!(suspend_count == 0))
144 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
145 if (mono_atomic_cas_i32 (&info->thread_state, STATE_DETACHED, raw_state) != raw_state)
146 goto retry_state_change;
147 trace_state_change ("DETACH", info, raw_state, STATE_DETACHED, 0);
148 return TRUE;
149 case STATE_ASYNC_SUSPEND_REQUESTED: //Can't detach until whoever asked us to suspend to be happy with us
150 return FALSE;
151
152 /*
153 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
154 STATE_SELF_SUSPENDED: Code should not be running while suspended.
155 STATE_SELF_SUSPEND_REQUESTED: This is a bug in the self suspend code that didn't execute the second part of it
156 STATE_BLOCKING_AND_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
157 */
158 default:
159 mono_fatal_with_history ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
160 }
161 }
162
163 /*
164 This transition initiates the suspension of another thread.
165
166 Returns one of the following values:
167
168 - AsyncSuspendInitSuspend: Thread suspend requested, async suspend needs to be done.
169 - AsyncSuspendAlreadySuspended: Thread already suspended, nothing to do.
170 - AsyncSuspendWait: Self suspend in progress, asked it to notify us. Caller must add target to the notification set.
171 - AsyncSuspendBlocking: Thread in blocking state
172 */
173 MonoRequestAsyncSuspendResult
mono_threads_transition_request_async_suspension(MonoThreadInfo * info)174 mono_threads_transition_request_async_suspension (MonoThreadInfo *info)
175 {
176 int raw_state, cur_state, suspend_count;
177 g_assert (info != mono_thread_info_current ());
178
179 retry_state_change:
180 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
181
182 switch (cur_state) {
183 case STATE_RUNNING: //Post an async suspend request
184 if (!(suspend_count == 0))
185 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
186 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1), raw_state) != raw_state)
187 goto retry_state_change;
188 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
189 return AsyncSuspendInitSuspend; //This is the first async suspend request against the target
190
191 case STATE_ASYNC_SUSPENDED:
192 case STATE_SELF_SUSPENDED: //Async suspend can suspend the same thread multiple times as it starts from the outside
193 case STATE_BLOCKING_AND_SUSPENDED:
194 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
195 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
196 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
197 goto retry_state_change;
198 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
199 return AsyncSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
200
201 case STATE_SELF_SUSPEND_REQUESTED: //This suspend needs to notify the initiator, so we need to promote the suspend to async
202 if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
203 mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
204 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, suspend_count + 1), raw_state) != raw_state)
205 goto retry_state_change;
206 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, 1);
207 return AsyncSuspendWait; //This is the first async suspend request, change the thread and let it notify us [1]
208
209 case STATE_BLOCKING:
210 if (!(suspend_count < THREAD_SUSPEND_COUNT_MAX))
211 mono_fatal_with_history ("suspend_count = %d, but should be < THREAD_SUSPEND_COUNT_MAX", suspend_count);
212 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (cur_state, suspend_count + 1), raw_state) != raw_state)
213 goto retry_state_change;
214 trace_state_change ("ASYNC_SUSPEND_REQUESTED", info, raw_state, cur_state, 1);
215 return AsyncSuspendBlocking; //A thread in the blocking state has its state saved so we can treat it as suspended.
216
217 /*
218
219 [1] It's questionable on what to do if we hit the beginning of a self suspend.
220 The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
221
222 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.
223 */
224 default:
225 mono_fatal_with_history ("Cannot transition thread %p from %s with ASYNC_SUSPEND_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
226 }
227 return (MonoRequestAsyncSuspendResult) FALSE;
228 }
229
230 /*
231 Check the current state of the thread and try to init a self suspend.
232 This must be called with self state saved.
233
234 Returns one of the following values:
235
236 - Resumed: Async resume happened and current thread should keep running
237 - Suspend: Caller should wait for a resume signal
238 - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
239 suspend should start.
240
241 */
242 MonoSelfSupendResult
mono_threads_transition_state_poll(MonoThreadInfo * info)243 mono_threads_transition_state_poll (MonoThreadInfo *info)
244 {
245 int raw_state, cur_state, suspend_count;
246 g_assert (mono_thread_info_is_current (info));
247
248 retry_state_change:
249 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
250 switch (cur_state) {
251 case STATE_RUNNING:
252 if (!(suspend_count == 0))
253 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
254 trace_state_change ("STATE_POLL", info, raw_state, cur_state, 0);
255 return SelfSuspendResumed; //We're fine, don't suspend
256
257 case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
258 case STATE_SELF_SUSPEND_REQUESTED: //Start the self suspend process
259 if (!(suspend_count > 0))
260 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
261 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count), raw_state) != raw_state)
262 goto retry_state_change;
263 trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, 0);
264 if (cur_state == STATE_SELF_SUSPEND_REQUESTED)
265 return SelfSuspendWait; //Caller should wait for resume
266 else
267 return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
268
269 /*
270 STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
271 STATE_SELF_SUSPENDED: Code should not be running while suspended.
272 STATE_BLOCKING:
273 STATE_BLOCKING_AND_SUSPENDED: Pool is a local state transition. No VM activities are allowed while in blocking mode.
274 */
275 default:
276 mono_fatal_with_history ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
277 }
278 }
279
280 /*
281 Try to resume a suspended thread.
282
283 Returns one of the following values:
284 - Sucess: The thread was resumed.
285 - Error: The thread was not suspended in the first place. [2]
286 - InitSelfResume: The thread is blocked on self suspend and should be resumed
287 - InitAsycResume: The thread is blocked on async suspend and should be resumed
288 - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
289
290 [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
291 used as a suspend permit and cancel each other.
292
293 Suspend permits are really useful to implement managed synchronization structures that
294 don't consume native resources. The downside is that they further complicate the design of this
295 system as the RUNNING state now has a non zero suspend counter.
296
297 It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
298
299 One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
300 This would make permits really harder to add.
301 */
302 MonoResumeResult
mono_threads_transition_request_resume(MonoThreadInfo * info)303 mono_threads_transition_request_resume (MonoThreadInfo* info)
304 {
305 int raw_state, cur_state, suspend_count;
306 g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
307
308 retry_state_change:
309 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
310 switch (cur_state) {
311 case STATE_RUNNING: //Thread already running.
312 if (!(suspend_count == 0))
313 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
314 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
315 return ResumeError; //Resume failed because thread was not blocked
316
317 case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
318 if (suspend_count == 0) {
319 trace_state_change ("RESUME", info, raw_state, cur_state, 0);
320 return ResumeError;
321 } else {
322 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
323 goto retry_state_change;
324 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
325 return ResumeOk; //Resume worked and there's nothing for the caller to do.
326 }
327 break;
328 case STATE_ASYNC_SUSPENDED:
329 case STATE_SELF_SUSPENDED:
330 case STATE_BLOCKING_AND_SUSPENDED: //Decrease the suspend_count and maybe resume
331 if (!(suspend_count > 0))
332 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
333 if (suspend_count > 1) {
334 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
335 goto retry_state_change;
336 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
337
338 return ResumeOk; //Resume worked and there's nothing for the caller to do.
339 } else {
340 if (mono_atomic_cas_i32 (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
341 goto retry_state_change;
342 trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
343
344 if (cur_state == STATE_ASYNC_SUSPENDED)
345 return ResumeInitAsyncResume; //Resume worked and caller must do async resume
346 else if (cur_state == STATE_SELF_SUSPENDED)
347 return ResumeInitSelfResume; //Resume worked and caller must do self resume
348 else
349 return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
350 }
351
352 case STATE_SELF_SUSPEND_REQUESTED: //Self suspend was requested but another thread decided to resume it.
353 if (!(suspend_count > 0))
354 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
355 if (suspend_count > 1) {
356 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (cur_state, suspend_count - 1), raw_state) != raw_state)
357 goto retry_state_change;
358 trace_state_change ("RESUME", info, raw_state, cur_state, -1);
359 } else {
360 if (mono_atomic_cas_i32 (&info->thread_state, STATE_RUNNING, raw_state) != raw_state)
361 goto retry_state_change;
362 trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, -1);
363 }
364 return ResumeOk; //Resume worked and there's nothing for the caller to do (the target never actually suspend).
365 /*
366
367 STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
368
369 [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
370 sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
371
372 [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
373 is similar to the one described in [2] when this is used for as a synchronization primitive.
374
375 If this turns to be a problem we should either implement [2] or make this an invalid transition.
376
377 */
378 default:
379 mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
380 }
381 }
382
383 /*
384 This performs the last step of async suspend.
385
386 Returns TRUE if the caller should wait for resume.
387 */
388 gboolean
mono_threads_transition_finish_async_suspend(MonoThreadInfo * info)389 mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
390 {
391 int raw_state, cur_state, suspend_count;
392
393 retry_state_change:
394 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
395 switch (cur_state) {
396
397 case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
398 case STATE_BLOCKING_AND_SUSPENDED: //async suspend raced with blocking and lost
399 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, 0);
400 return FALSE; //let self suspend wait
401
402 case STATE_ASYNC_SUSPEND_REQUESTED:
403 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count), raw_state) != raw_state)
404 goto retry_state_change;
405 trace_state_change ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, 0);
406 return TRUE; //Async suspend worked, now wait for resume
407
408 /*
409 STATE_RUNNING: A thread cannot escape suspension once requested.
410 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.
411 STATE_SELF_SUSPEND_REQUESTED: When self suspend and async suspend happen together, they converge to async suspend so this state should not be visible.
412 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.
413 */
414 default:
415 mono_fatal_with_history ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
416 }
417 }
418
419 /*
420 This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
421
422 Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
423 That state only works as long as the only managed state touched is blitable and was pinned before the transition.
424
425 It returns the action the caller must perform:
426
427 - Continue: Entered blocking state sucessfully;
428 - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
429
430 */
431 MonoDoBlockingResult
mono_threads_transition_do_blocking(MonoThreadInfo * info)432 mono_threads_transition_do_blocking (MonoThreadInfo* info)
433 {
434 int raw_state, cur_state, suspend_count;
435
436 retry_state_change:
437 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
438 switch (cur_state) {
439
440 case STATE_RUNNING: //transition to blocked
441 if (!(suspend_count == 0))
442 mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
443 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count), raw_state) != raw_state)
444 goto retry_state_change;
445 trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, 0);
446 return DoBlockingContinue;
447
448 case STATE_ASYNC_SUSPEND_REQUESTED:
449 if (!(suspend_count > 0))
450 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
451 trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, 0);
452 return DoBlockingPollAndRetry;
453 /*
454 STATE_ASYNC_SUSPENDED
455 STATE_SELF_SUSPENDED: Code should not be running while suspended.
456 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
457 STATE_BLOCKING:
458 STATE_BLOCKING_AND_SUSPENDED: Blocking is not nestabled
459 */
460 default:
461 mono_fatal_with_history ("Cannot transition thread %p from %s with DO_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
462 }
463 }
464
465 /*
466 This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
467 until its resumed before continuing.
468
469 It returns one of:
470 -Aborted: The blocking operation was aborted and not properly restored. Aborts can happen due to lazy loading and some n2m transitions;
471 -Ok: Done with blocking, just move on;
472 -Wait: This thread was async suspended, wait for resume
473
474 */
475 MonoDoneBlockingResult
mono_threads_transition_done_blocking(MonoThreadInfo * info)476 mono_threads_transition_done_blocking (MonoThreadInfo* info)
477 {
478 int raw_state, cur_state, suspend_count;
479
480 retry_state_change:
481 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
482 switch (cur_state) {
483 case STATE_BLOCKING:
484 if (suspend_count == 0) {
485 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
486 goto retry_state_change;
487 trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, 0);
488 return DoneBlockingOk;
489 } else {
490 if (!(suspend_count >= 0))
491 mono_fatal_with_history ("suspend_count = %d, but should be >= 0", suspend_count);
492 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_BLOCKING_AND_SUSPENDED, suspend_count), raw_state) != raw_state)
493 goto retry_state_change;
494 trace_state_change ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_AND_SUSPENDED, 0);
495 return DoneBlockingWait;
496 }
497
498 /*
499 STATE_RUNNING: //Blocking was aborted and not properly restored
500 STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
501 STATE_ASYNC_SUSPENDED
502 STATE_SELF_SUSPENDED: Code should not be running while suspended.
503 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend
504 STATE_BLOCKING_AND_SUSPENDED: This an exit state of done blocking
505 */
506 default:
507 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
508 }
509 }
510
511 /*
512 Transition a thread in what should be a blocking state back to running state.
513 This is different that done blocking because the goal is to get back to blocking once we're done.
514 This is required to be able to bail out of blocking in case we're back to inside the runtime.
515
516 It returns one of:
517 -Ignore: Thread was not in blocking, nothing to do;
518 -IgnoreAndPool: Thread was not blocking and there's a pending suspend that needs to be processed;
519 -Ok: Blocking state successfully aborted;
520 -Wait: Blocking state successfully aborted, there's a pending suspend to be processed though
521 */
522 MonoAbortBlockingResult
mono_threads_transition_abort_blocking(THREAD_INFO_TYPE * info)523 mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info)
524 {
525 int raw_state, cur_state, suspend_count;
526
527 retry_state_change:
528 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
529 switch (cur_state) {
530 case STATE_RUNNING: //thread already in runnable state
531 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
532 return AbortBlockingIgnore;
533
534 case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
535 trace_state_change ("ABORT_BLOCKING", info, raw_state, cur_state, 0);
536 return AbortBlockingIgnoreAndPoll;
537
538 case STATE_BLOCKING:
539 if (suspend_count == 0) {
540 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count), raw_state) != raw_state)
541 goto retry_state_change;
542 trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, 0);
543 return AbortBlockingOk;
544 } else {
545 if (!(suspend_count > 0))
546 mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
547 if (mono_atomic_cas_i32 (&info->thread_state, build_thread_state (STATE_BLOCKING_AND_SUSPENDED, suspend_count), raw_state) != raw_state)
548 goto retry_state_change;
549 trace_state_change ("ABORT_BLOCKING", info, raw_state, STATE_BLOCKING_AND_SUSPENDED, 0);
550 return AbortBlockingWait;
551 }
552 /*
553 STATE_ASYNC_SUSPENDED:
554 STATE_SELF_SUSPENDED: Code should not be running while suspended.
555 STATE_SELF_SUSPEND_REQUESTED: A blocking operation must not be done while trying to self suspend.
556 STATE_BLOCKING_AND_SUSPENDED: This is an exit state of done blocking, can't happen here.
557 */
558 default:
559 mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
560 }
561 }
562
563 MonoThreadUnwindState*
mono_thread_info_get_suspend_state(MonoThreadInfo * info)564 mono_thread_info_get_suspend_state (MonoThreadInfo *info)
565 {
566 int raw_state, cur_state, suspend_count;
567 UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
568 switch (cur_state) {
569 case STATE_ASYNC_SUSPENDED:
570 return &info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX];
571 case STATE_SELF_SUSPENDED:
572 case STATE_BLOCKING_AND_SUSPENDED:
573 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
574 case STATE_BLOCKING:
575 if (suspend_count > 0)
576 return &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
577 default:
578 /*
579 STATE_RUNNING
580 STATE_SELF_SUSPENDED
581 STATE_ASYNC_SUSPEND_REQUESTED
582 STATE_BLOCKING: All those are invalid suspend states.
583 */
584 g_error ("Cannot read suspend state when target %p is in the %s state", mono_thread_info_get_tid (info), state_name (cur_state));
585 }
586 }
587
588 // State checking code
589 /**
590 * Return TRUE is the thread is in a runnable state.
591 */
592 gboolean
mono_thread_info_is_running(MonoThreadInfo * info)593 mono_thread_info_is_running (MonoThreadInfo *info)
594 {
595 switch (get_thread_state (info->thread_state)) {
596 case STATE_RUNNING:
597 case STATE_ASYNC_SUSPEND_REQUESTED:
598 case STATE_SELF_SUSPEND_REQUESTED:
599 case STATE_BLOCKING:
600 return TRUE;
601 }
602 return FALSE;
603 }
604
605 /**
606 * Return TRUE is the thread is in an usable (suspendable) state
607 */
608 gboolean
mono_thread_info_is_live(MonoThreadInfo * info)609 mono_thread_info_is_live (MonoThreadInfo *info)
610 {
611 switch (get_thread_state (info->thread_state)) {
612 case STATE_STARTING:
613 case STATE_DETACHED:
614 return FALSE;
615 }
616 return TRUE;
617 }
618
619 int
mono_thread_info_suspend_count(MonoThreadInfo * info)620 mono_thread_info_suspend_count (MonoThreadInfo *info)
621 {
622 return get_thread_suspend_count (info->thread_state);
623 }
624
625 int
mono_thread_info_current_state(MonoThreadInfo * info)626 mono_thread_info_current_state (MonoThreadInfo *info)
627 {
628 return get_thread_state (info->thread_state);
629 }
630
631 const char*
mono_thread_state_name(int state)632 mono_thread_state_name (int state)
633 {
634 return state_name (state);
635 }
636
637 gboolean
mono_thread_is_gc_unsafe_mode(void)638 mono_thread_is_gc_unsafe_mode (void)
639 {
640 MonoThreadInfo *cur = mono_thread_info_current ();
641
642 if (!cur)
643 return FALSE;
644
645 switch (mono_thread_info_current_state (cur)) {
646 case STATE_RUNNING:
647 case STATE_ASYNC_SUSPEND_REQUESTED:
648 case STATE_SELF_SUSPEND_REQUESTED:
649 return TRUE;
650 default:
651 return FALSE;
652 }
653 }
654