mono-threads-state-machine.c 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
  1. /**
  2. * \file
  3. */
  4. #include <config.h>
  5. #include <mono/utils/mono-compiler.h>
  6. #include <mono/utils/mono-threads.h>
  7. #include <mono/utils/mono-tls.h>
  8. #include <mono/utils/mono-memory-model.h>
  9. #include <mono/utils/atomic.h>
  10. #include <mono/utils/checked-build.h>
  11. #include <mono/utils/mono-threads-debug.h>
  12. #include <errno.h>
  13. /*thread state helpers*/
  14. static int
  15. get_thread_state (int thread_state)
  16. {
  17. const MonoThreadStateMachine state = {thread_state};
  18. return state.state;
  19. }
  20. #if defined (THREADS_STATE_MACHINE_DEBUG_ENABLED) || defined (ENABLE_CHECKED_BUILD_THREAD)
  21. static int
  22. get_thread_suspend_count (int thread_state)
  23. {
  24. const MonoThreadStateMachine state = {thread_state};
  25. return state.suspend_count;
  26. }
  27. #endif
  28. #ifdef THREADS_STATE_MACHINE_DEBUG_ENABLED
  29. static gboolean
  30. get_thread_no_safepoints (int thread_state)
  31. {
  32. const MonoThreadStateMachine state = {thread_state};
  33. return state.no_safepoints;
  34. }
  35. #endif
  36. static MonoThreadStateMachine
  37. build_thread_state (int thread_state, int suspend_count, gboolean no_safepoints)
  38. {
  39. g_assert (suspend_count >= 0 && suspend_count <= THREAD_SUSPEND_COUNT_MAX);
  40. g_assert (thread_state >= 0 && thread_state <= STATE_MAX);
  41. no_safepoints = !!no_safepoints; // ensure it's 0 or 1
  42. /* need a predictable value for the unused bits so that
  43. * thread_state_cas does not fail.
  44. */
  45. MonoThreadStateMachine state = { 0 };
  46. state.state = thread_state;
  47. state.no_safepoints = no_safepoints;
  48. state.suspend_count = suspend_count;
  49. return state;
  50. }
  51. static int
  52. thread_state_cas (MonoThreadStateMachine *state, MonoThreadStateMachine new_value, int old_raw)
  53. {
  54. return mono_atomic_cas_i32 (&state->raw, new_value.raw, old_raw);
  55. }
  56. static const char*
  57. state_name (int state)
  58. {
  59. static const char *state_names [] = {
  60. "STARTING",
  61. "DETACHED",
  62. "RUNNING",
  63. "ASYNC_SUSPENDED",
  64. "SELF_SUSPENDED",
  65. "ASYNC_SUSPEND_REQUESTED",
  66. "STATE_BLOCKING",
  67. "STATE_BLOCKING_ASYNC_SUSPENDED",
  68. "STATE_BLOCKING_SELF_SUSPENDED",
  69. "STATE_BLOCKING_SUSPEND_REQUESTED",
  70. };
  71. return state_names [get_thread_state (state)];
  72. }
  73. static void
  74. unwrap_thread_state (MonoThreadInfo* info,
  75. int *raw,
  76. int *cur,
  77. int *count,
  78. int *blk)
  79. {
  80. g_static_assert (sizeof (MonoThreadStateMachine) == sizeof (int32_t));
  81. const MonoThreadStateMachine state = {mono_atomic_load_i32 (&info->thread_state.raw)};
  82. // Read once from info and then read from local to get consistent values.
  83. *raw = state.raw;
  84. *cur = state.state;
  85. *count = state.suspend_count;
  86. *blk = state.no_safepoints;
  87. }
  88. #define UNWRAP_THREAD_STATE(RAW,CUR,COUNT,BLK,INFO) \
  89. unwrap_thread_state ((INFO), &(RAW), &(CUR), &(COUNT), &(BLK))
  90. static void
  91. check_thread_state (MonoThreadInfo* info)
  92. {
  93. int raw_state, cur_state, suspend_count;
  94. gboolean no_safepoints;
  95. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  96. switch (cur_state) {
  97. case STATE_STARTING:
  98. case STATE_DETACHED:
  99. g_assert (!no_safepoints);
  100. /* fallthru */
  101. case STATE_RUNNING:
  102. g_assert (suspend_count == 0);
  103. break;
  104. case STATE_BLOCKING_SELF_SUSPENDED:
  105. case STATE_BLOCKING_SUSPEND_REQUESTED:
  106. case STATE_BLOCKING_ASYNC_SUSPENDED:
  107. case STATE_ASYNC_SUSPENDED:
  108. case STATE_SELF_SUSPENDED:
  109. g_assert (!no_safepoints);
  110. /* fallthru */
  111. case STATE_ASYNC_SUSPEND_REQUESTED:
  112. g_assert (suspend_count > 0);
  113. break;
  114. case STATE_BLOCKING:
  115. g_assert (!no_safepoints);
  116. g_assert (suspend_count == 0);
  117. break;
  118. default:
  119. g_error ("Invalid state %d", cur_state);
  120. }
  121. }
  122. static void
  123. 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)
  124. {
  125. check_thread_state (info);
  126. THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s %s -> %s %s (%d -> %d) %s\n",
  127. transition,
  128. mono_thread_info_get_tid (info),
  129. state_name (get_thread_state (cur_raw_state)),
  130. (get_thread_no_safepoints (cur_raw_state) ? "X" : "."),
  131. state_name (next_state),
  132. (next_no_safepoints ? "X" : "."),
  133. get_thread_suspend_count (cur_raw_state),
  134. get_thread_suspend_count (cur_raw_state) + suspend_count_delta,
  135. func);
  136. CHECKED_BUILD_THREAD_TRANSITION (transition, info, get_thread_state (cur_raw_state), get_thread_suspend_count (cur_raw_state), next_state, suspend_count_delta);
  137. }
  138. static void
  139. 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)
  140. {
  141. check_thread_state (info);
  142. THREADS_STATE_MACHINE_DEBUG ("[%s][%p] %s %s -> %s %s (%d -> %d) %s\n",
  143. transition,
  144. mono_thread_info_get_tid (info),
  145. state_name (get_thread_state (cur_raw_state)),
  146. (get_thread_no_safepoints (cur_raw_state) ? "X" : "."),
  147. state_name (next_state),
  148. (next_no_safepoints ? "X" : "."),
  149. get_thread_suspend_count (cur_raw_state),
  150. get_thread_suspend_count (cur_raw_state) + suspend_count_delta,
  151. func);
  152. 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);
  153. }
  154. static void
  155. trace_state_change (const char *transition, MonoThreadInfo *info, int cur_raw_state, int next_state, gboolean next_no_safepoints, int suspend_count_delta)
  156. // FIXME migrate all uses
  157. {
  158. trace_state_change_with_func (transition, info, cur_raw_state, next_state, next_no_safepoints, suspend_count_delta, "");
  159. }
  160. /*
  161. This is the transition that signals that a thread is functioning.
  162. Its main goal is to catch threads been witnessed before been fully registered.
  163. */
  164. void
  165. mono_threads_transition_attach (MonoThreadInfo* info)
  166. {
  167. int raw_state, cur_state, suspend_count;
  168. gboolean no_safepoints;
  169. retry_state_change:
  170. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  171. switch (cur_state) {
  172. case STATE_STARTING:
  173. if (!(suspend_count == 0))
  174. mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
  175. if (no_safepoints)
  176. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  177. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, 0, 0), raw_state) != raw_state)
  178. goto retry_state_change;
  179. trace_state_change ("ATTACH", info, raw_state, STATE_RUNNING, FALSE, 0);
  180. break;
  181. default:
  182. mono_fatal_with_history ("Cannot transition current thread from %s with ATTACH", state_name (cur_state));
  183. }
  184. }
  185. /*
  186. This is the transition that signals that a thread is no longer registered with the runtime.
  187. Its main goal is to catch threads been witnessed after they detach.
  188. This returns TRUE is the transition succeeded.
  189. If it returns false it means that there's a pending suspend that should be acted upon.
  190. */
  191. gboolean
  192. mono_threads_transition_detach (MonoThreadInfo *info)
  193. {
  194. int raw_state, cur_state, suspend_count;
  195. gboolean no_safepoints;
  196. retry_state_change:
  197. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  198. switch (cur_state) {
  199. case STATE_RUNNING:
  200. case STATE_BLOCKING: /* An OS thread on coop goes STARTING->BLOCKING->RUNNING->BLOCKING->DETACHED */
  201. if (!(suspend_count == 0))
  202. mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
  203. if (no_safepoints)
  204. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  205. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_DETACHED, 0, 0), raw_state) != raw_state)
  206. goto retry_state_change;
  207. trace_state_change ("DETACH", info, raw_state, STATE_DETACHED, FALSE, 0);
  208. return TRUE;
  209. case STATE_ASYNC_SUSPEND_REQUESTED: //Can't detach until whoever asked us to suspend to be happy with us
  210. case STATE_BLOCKING_SUSPEND_REQUESTED:
  211. return FALSE;
  212. /*
  213. STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
  214. STATE_SELF_SUSPENDED: Code should not be running while suspended.
  215. STATE_BLOCKING_SELF_SUSPENDED: This is a bug in coop x suspend that resulted the thread in an undetachable state.
  216. STATE_BLOCKING_ASYNC_SUSPENDED: Same as BLOCKING_SELF_SUSPENDED
  217. */
  218. default:
  219. mono_fatal_with_history ("Cannot transition current thread %p from %s with DETACH", info, state_name (cur_state));
  220. }
  221. }
  222. /*
  223. This transition initiates the suspension of another thread.
  224. Returns one of the following values:
  225. - ReqSuspendInitSuspendRunning: Thread suspend requested, caller must initiate suspend.
  226. - ReqSuspendInitSuspendBlocking: Thread in blocking state, caller may initiate suspend.
  227. - ReqSuspendAlreadySuspended: Thread was already suspended and not executing, nothing to do.
  228. - ReqSuspendAlreadySuspendedBlocking: Thread was already in blocking and a suspend was requested
  229. and the thread is still executing (perhaps in a syscall),
  230. nothing to do.
  231. */
  232. MonoRequestSuspendResult
  233. mono_threads_transition_request_suspension (MonoThreadInfo *info)
  234. {
  235. int raw_state, cur_state, suspend_count;
  236. gboolean no_safepoints;
  237. g_assert (info != mono_thread_info_current ());
  238. retry_state_change:
  239. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  240. switch (cur_state) {
  241. case STATE_RUNNING: //Post an async suspend request
  242. if (!(suspend_count == 0))
  243. mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
  244. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPEND_REQUESTED, 1, no_safepoints), raw_state) != raw_state)
  245. goto retry_state_change;
  246. trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, STATE_ASYNC_SUSPEND_REQUESTED, no_safepoints, 1);
  247. return ReqSuspendInitSuspendRunning; //This is the first async suspend request against the target
  248. case STATE_BLOCKING_SELF_SUSPENDED:
  249. case STATE_BLOCKING_ASYNC_SUSPENDED:
  250. case STATE_ASYNC_SUSPENDED:
  251. case STATE_SELF_SUSPENDED:
  252. if (no_safepoints)
  253. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  254. if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
  255. mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
  256. if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count + 1, no_safepoints), raw_state) != raw_state)
  257. goto retry_state_change;
  258. trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, cur_state, no_safepoints, 1);
  259. return ReqSuspendAlreadySuspended; //Thread is already suspended so we don't need to wait it to suspend
  260. case STATE_BLOCKING:
  261. if (!(suspend_count == 0))
  262. mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
  263. if (no_safepoints)
  264. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  265. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SUSPEND_REQUESTED, 1, no_safepoints), raw_state) != raw_state)
  266. goto retry_state_change;
  267. trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, STATE_BLOCKING_SUSPEND_REQUESTED, no_safepoints, 1);
  268. return ReqSuspendInitSuspendBlocking; //A thread in the blocking state has its state saved so we can treat it as suspended.
  269. case STATE_BLOCKING_SUSPEND_REQUESTED:
  270. /* This should only be happening if we're doing a cooperative suspend of a blocking thread.
  271. * In which case we could be in BLOCKING_SUSPEND_REQUESTED until we execute a done or abort blocking.
  272. * In preemptive suspend of a blocking thread since there's a single suspend initiator active at a time,
  273. * we would expect a finish_async_suspension or a done/abort blocking before the next suspension request
  274. */
  275. if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
  276. mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
  277. if (no_safepoints)
  278. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  279. if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count + 1, no_safepoints), raw_state) != raw_state)
  280. goto retry_state_change;
  281. trace_state_change ("SUSPEND_INIT_REQUESTED", info, raw_state, cur_state, no_safepoints, 1);
  282. return ReqSuspendAlreadySuspendedBlocking;
  283. /*
  284. [1] It's questionable on what to do if we hit the beginning of a self suspend.
  285. The expected behavior is that the target should poll its state very soon so the the suspend latency should be minimal.
  286. 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.
  287. */
  288. default:
  289. mono_fatal_with_history ("Cannot transition thread %p from %s with SUSPEND_INIT_REQUESTED", mono_thread_info_get_tid (info), state_name (cur_state));
  290. }
  291. return (MonoRequestSuspendResult) FALSE;
  292. }
  293. /*
  294. Peek at the thread state and return whether it's BLOCKING_SUSPEND_REQUESTED or not.
  295. Assumes that it is called in the second phase of a two-phase suspend, so the
  296. thread is either some flavor of suspended or else blocking suspend requested.
  297. All other states can't happen.
  298. */
  299. gboolean
  300. mono_threads_transition_peek_blocking_suspend_requested (MonoThreadInfo *info)
  301. {
  302. int raw_state, cur_state, suspend_count;
  303. gboolean no_safepoints;
  304. g_assert (info != mono_thread_info_current ());
  305. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  306. switch (cur_state) {
  307. case STATE_ASYNC_SUSPENDED:
  308. case STATE_SELF_SUSPENDED:
  309. return FALSE; /*ReqPeekBlockingSuspendRequestedRunningSuspended;*/
  310. case STATE_BLOCKING_SELF_SUSPENDED:
  311. case STATE_BLOCKING_ASYNC_SUSPENDED:
  312. case STATE_BLOCKING_SUSPEND_REQUESTED:
  313. if (!(suspend_count > 0 && suspend_count < THREAD_SUSPEND_COUNT_MAX))
  314. mono_fatal_with_history ("suspend_count = %d, but should be > 0 and < THREAD_SUSPEND_COUNT_MAX", suspend_count);
  315. if (no_safepoints)
  316. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  317. if (cur_state == STATE_BLOCKING_SUSPEND_REQUESTED)
  318. return TRUE; /*ReqPeekBlockingSuspendRequestedBlockingSuspendRequested;*/
  319. else
  320. return FALSE; /*ReqPeekBlockingSuspendRequestedBlockingSuspended;*/
  321. /*
  322. STATE_RUNNING:
  323. Can't happen - should have been suspended in the first phase.
  324. STATE_ASYNC_SUSPEND_REQUESTED
  325. Can't happen - first phase should've waited until the thread self-suspended
  326. STATE_BLOCKING:
  327. Can't happen - should've had a suspension request in the first phase.
  328. */
  329. default:
  330. 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));
  331. }
  332. }
  333. /*
  334. Check the current state of the thread and try to init a self suspend.
  335. This must be called with self state saved.
  336. Returns one of the following values:
  337. - Resumed: Async resume happened and current thread should keep running
  338. - Suspend: Caller should wait for a resume signal
  339. - SelfSuspendNotifyAndWait: Notify the suspend initiator and wait for a resume signals
  340. suspend should start.
  341. */
  342. MonoSelfSupendResult
  343. mono_threads_transition_state_poll (MonoThreadInfo *info)
  344. {
  345. int raw_state, cur_state, suspend_count;
  346. gboolean no_safepoints;
  347. g_assert (mono_thread_info_is_current (info));
  348. retry_state_change:
  349. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  350. switch (cur_state) {
  351. case STATE_RUNNING:
  352. if (no_safepoints)
  353. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in RUNNING with STATE_POLL");
  354. if (!(suspend_count == 0))
  355. mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
  356. trace_state_change ("STATE_POLL", info, raw_state, cur_state, no_safepoints, 0);
  357. return SelfSuspendResumed; //We're fine, don't suspend
  358. case STATE_ASYNC_SUSPEND_REQUESTED: //Async suspend requested, service it with a self suspend
  359. if (no_safepoints)
  360. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in ASYNS_SUSPEND_REQUESTED with STATE_POLL");
  361. if (!(suspend_count > 0))
  362. mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
  363. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_SELF_SUSPENDED, suspend_count, no_safepoints), raw_state) != raw_state)
  364. goto retry_state_change;
  365. trace_state_change ("STATE_POLL", info, raw_state, STATE_SELF_SUSPENDED, no_safepoints, 0);
  366. return SelfSuspendNotifyAndWait; //Caller should notify suspend initiator and wait for resume
  367. /*
  368. STATE_ASYNC_SUSPENDED: Code should not be running while suspended.
  369. STATE_SELF_SUSPENDED: Code should not be running while suspended.
  370. STATE_BLOCKING:
  371. STATE_BLOCKING_SUSPEND_REQUESTED:
  372. STATE_BLOCKING_ASYNC_SUSPENDED:
  373. STATE_BLOCKING_SELF_SUSPENDED: Poll is a local state transition. No VM activities are allowed while in blocking mode.
  374. (In all the blocking states - the local thread has no checkpoints, hence
  375. no polling, it can only do abort blocking or done blocking on itself).
  376. */
  377. default:
  378. mono_fatal_with_history ("Cannot transition thread %p from %s with STATE_POLL", mono_thread_info_get_tid (info), state_name (cur_state));
  379. }
  380. }
  381. /*
  382. Try to resume a suspended thread.
  383. Returns one of the following values:
  384. - Sucess: The thread was resumed.
  385. - Error: The thread was not suspended in the first place. [2]
  386. - InitSelfResume: The thread is blocked on self suspend and should be resumed
  387. - InitAsyncResume: The thread is blocked on async suspend and should be resumed
  388. - ResumeInitBlockingResume: The thread was suspended on the exit path of blocking state and should be resumed
  389. FIXME: ResumeInitBlockingResume is just InitSelfResume by a different name.
  390. [2] This threading system uses an unsigned suspend count. Which means a resume cannot be
  391. used as a suspend permit and cancel each other.
  392. Suspend permits are really useful to implement managed synchronization structures that
  393. don't consume native resources. The downside is that they further complicate the design of this
  394. system as the RUNNING state now has a non zero suspend counter.
  395. It can be implemented in the future if we find resume/suspend races that cannot be (efficiently) fixed by other means.
  396. One major issue with suspend permits is runtime facilities (GC, debugger) that must have the target suspended when requested.
  397. This would make permits really harder to add.
  398. */
  399. MonoResumeResult
  400. mono_threads_transition_request_resume (MonoThreadInfo* info)
  401. {
  402. int raw_state, cur_state, suspend_count;
  403. gboolean no_safepoints;
  404. g_assert (info != mono_thread_info_current ()); //One can't self resume [3]
  405. retry_state_change:
  406. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  407. switch (cur_state) {
  408. case STATE_RUNNING: //Thread already running.
  409. if (!(suspend_count == 0))
  410. mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
  411. trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, 0);
  412. return ResumeError; //Resume failed because thread was not blocked
  413. case STATE_BLOCKING: //Blocking, might have a suspend count, we decrease if it's > 0
  414. if (!(suspend_count == 0))
  415. mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
  416. if (no_safepoints)
  417. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  418. trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, 0);
  419. return ResumeError;
  420. case STATE_BLOCKING_SUSPEND_REQUESTED:
  421. if (!(suspend_count > 0))
  422. mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
  423. if (no_safepoints)
  424. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  425. if (suspend_count > 1) {
  426. if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
  427. goto retry_state_change;
  428. trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, -1);
  429. return ResumeOk; //Resume worked and there's nothing for the caller to do.
  430. } else {
  431. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING, 0, 0), raw_state) != raw_state)
  432. goto retry_state_change;
  433. trace_state_change ("RESUME", info, raw_state, STATE_BLOCKING, no_safepoints, -1);
  434. return ResumeOk; // Resume worked, back in blocking, nothing for the caller to do.
  435. }
  436. case STATE_BLOCKING_ASYNC_SUSPENDED:
  437. if (!(suspend_count > 0))
  438. mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
  439. if (no_safepoints)
  440. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  441. if (suspend_count > 1) {
  442. if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
  443. goto retry_state_change;
  444. trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, -1);
  445. return ResumeOk; // Resume worked, there's nothing else for the caller to do.
  446. } else {
  447. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING, 0, 0), raw_state) != raw_state)
  448. goto retry_state_change;
  449. trace_state_change ("RESUME", info, raw_state, STATE_BLOCKING, no_safepoints, -1);
  450. return ResumeInitAsyncResume; // Resume worked and caller must do async resume, thread resumes in BLOCKING
  451. }
  452. case STATE_BLOCKING_SELF_SUSPENDED: //Decrease the suspend_count and maybe resume
  453. case STATE_ASYNC_SUSPENDED:
  454. case STATE_SELF_SUSPENDED:
  455. if (no_safepoints)
  456. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  457. if (!(suspend_count > 0))
  458. mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
  459. if (suspend_count > 1) {
  460. if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
  461. goto retry_state_change;
  462. trace_state_change ("RESUME", info, raw_state, cur_state, no_safepoints, -1);
  463. return ResumeOk; //Resume worked and there's nothing for the caller to do.
  464. } else {
  465. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, 0, no_safepoints), raw_state) != raw_state)
  466. goto retry_state_change;
  467. trace_state_change ("RESUME", info, raw_state, STATE_RUNNING, no_safepoints, -1);
  468. if (cur_state == STATE_ASYNC_SUSPENDED)
  469. return ResumeInitAsyncResume; //Resume worked and caller must do async resume
  470. else if (cur_state == STATE_SELF_SUSPENDED)
  471. return ResumeInitSelfResume; //Resume worked and caller must do self resume
  472. else
  473. return ResumeInitBlockingResume; //Resume worked and caller must do blocking resume
  474. }
  475. /*
  476. STATE_ASYNC_SUSPEND_REQUESTED: Only one async suspend/resume operation can be in flight, so a resume cannot witness an internal state of suspend
  477. [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
  478. sense as a suspend permit, but as explained in [2] we don't support it so this is a bug.
  479. [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
  480. is similar to the one described in [2] when this is used for as a synchronization primitive.
  481. If this turns to be a problem we should either implement [2] or make this an invalid transition.
  482. */
  483. default:
  484. mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_RESUME", mono_thread_info_get_tid (info), state_name (cur_state));
  485. }
  486. }
  487. /*
  488. Try to resume a suspended thread and atomically request that it suspend again.
  489. Returns one of the following values:
  490. - InitAsyncPulse: The thread is suspended with preemptive suspend and should be resumed.
  491. */
  492. MonoPulseResult
  493. mono_threads_transition_request_pulse (MonoThreadInfo* info)
  494. {
  495. int raw_state, cur_state, suspend_count;
  496. gboolean no_safepoints;
  497. g_assert (info != mono_thread_info_current ()); //One can't self pulse [3]
  498. retry_state_change:
  499. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  500. switch (cur_state) {
  501. case STATE_BLOCKING_ASYNC_SUSPENDED:
  502. if (!(suspend_count == 1))
  503. mono_fatal_with_history ("suspend_count = %d, but should be == 1", suspend_count);
  504. if (no_safepoints)
  505. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  506. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SUSPEND_REQUESTED, suspend_count, no_safepoints), raw_state) != raw_state)
  507. goto retry_state_change;
  508. trace_state_change ("PULSE", info, raw_state, STATE_BLOCKING_SUSPEND_REQUESTED, no_safepoints, -1);
  509. return PulseInitAsyncPulse; // Pulse worked and caller must do async pulse, thread pulses in BLOCKING
  510. /*
  511. STATE_RUNNING:
  512. STATE_BLOCKING:
  513. Only one suspend initiator at a time. Current STW stopped the
  514. thread and now needs to resume it. So thread must be in one of the suspended
  515. states if we get here.
  516. STATE_BLOCKING_SUSPEND_REQUESTED:
  517. STATE_ASYNC_SUSPEND_REQUESTED:
  518. Only one pulse operation can be in flight, so a pulse cannot witness an
  519. internal state of suspend
  520. STATE_ASYNC_SUSPENDED:
  521. Hybrid suspend shouldn't put GC Unsafe threads into async suspended state.
  522. STATE_BLOCKING_SELF_SUSPENDED:
  523. STATE_SELF_SUSPENDED:
  524. Don't expect these to be pulsed - they're not problematic.
  525. */
  526. default:
  527. mono_fatal_with_history ("Cannot transition thread %p from %s with REQUEST_PULSE", mono_thread_info_get_tid (info), state_name (cur_state));
  528. }
  529. }
  530. /*
  531. Abort last step of preemptive suspend in case of failure to async suspend thread.
  532. This function makes sure state machine reflects current state of thread (running/suspended)
  533. in case of failure to complete async suspend of thread. NOTE, thread can still have reached
  534. a suspend state (in case of self-suspend).
  535. Returns TRUE if async suspend request was successfully aborted. Thread should be in STATE_RUNNING.
  536. Returns FALSE if async suspend request was successfully aborted but thread already reached self-suspended.
  537. */
  538. gboolean
  539. mono_threads_transition_abort_async_suspend (MonoThreadInfo* info)
  540. {
  541. int raw_state, cur_state, suspend_count;
  542. gboolean no_safepoints;
  543. retry_state_change:
  544. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  545. switch (cur_state) {
  546. case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
  547. case STATE_BLOCKING_SELF_SUSPENDED: //async suspend raced with blocking and lost
  548. if (no_safepoints)
  549. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  550. trace_state_change_sigsafe ("ABORT_ASYNC_SUSPEND", info, raw_state, cur_state, no_safepoints, 0, "");
  551. return FALSE; //thread successfully reached suspend state.
  552. case STATE_ASYNC_SUSPEND_REQUESTED:
  553. case STATE_BLOCKING_SUSPEND_REQUESTED:
  554. if (!(suspend_count > 0))
  555. mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
  556. if (no_safepoints)
  557. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  558. if (suspend_count > 1) {
  559. if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count - 1, no_safepoints), raw_state) != raw_state)
  560. goto retry_state_change;
  561. trace_state_change ("ABORT_ASYNC_SUSPEND", info, raw_state, cur_state, no_safepoints, -1);
  562. } else {
  563. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, 0, no_safepoints), raw_state) != raw_state)
  564. goto retry_state_change;
  565. trace_state_change ("ABORT_ASYNC_SUSPEND", info, raw_state, STATE_RUNNING, no_safepoints, -1);
  566. }
  567. return TRUE; //aborting thread suspend request succeded, thread is running.
  568. /*
  569. STATE_RUNNING: A thread cannot escape suspension once requested.
  570. 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.
  571. 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.
  572. With cooperative suspend, there are no finish_async_suspend transitions since there's no path back from asyns_suspend requested to running.
  573. 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.
  574. */
  575. default:
  576. mono_fatal_with_history ("Cannot transition thread %p from %s with ABORT_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
  577. }
  578. }
  579. /*
  580. This performs the last step of preemptive suspend.
  581. Returns TRUE if the caller should wait for resume.
  582. */
  583. gboolean
  584. mono_threads_transition_finish_async_suspend (MonoThreadInfo* info)
  585. {
  586. int raw_state, cur_state, suspend_count;
  587. gboolean no_safepoints;
  588. retry_state_change:
  589. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  590. switch (cur_state) {
  591. case STATE_SELF_SUSPENDED: //async suspend raced with self suspend and lost
  592. case STATE_BLOCKING_SELF_SUSPENDED: //async suspend raced with blocking and lost
  593. if (no_safepoints)
  594. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  595. trace_state_change_sigsafe ("FINISH_ASYNC_SUSPEND", info, raw_state, cur_state, no_safepoints, 0, "");
  596. return FALSE; //let self suspend wait
  597. case STATE_ASYNC_SUSPEND_REQUESTED:
  598. if (!(suspend_count > 0))
  599. mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
  600. /* Don't expect to see no_safepoints, ever, with async */
  601. if (no_safepoints)
  602. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in ASYNC_SUSPEND_REQUESTED with FINISH_ASYNC_SUSPEND");
  603. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_ASYNC_SUSPENDED, suspend_count, FALSE), raw_state) != raw_state)
  604. goto retry_state_change;
  605. trace_state_change_sigsafe ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_ASYNC_SUSPENDED, FALSE, 0, "");
  606. return TRUE; //Async suspend worked, now wait for resume
  607. case STATE_BLOCKING_SUSPEND_REQUESTED:
  608. if (!(suspend_count > 0))
  609. mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
  610. if (no_safepoints)
  611. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  612. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_ASYNC_SUSPENDED, suspend_count, FALSE), raw_state) != raw_state)
  613. goto retry_state_change;
  614. trace_state_change_sigsafe ("FINISH_ASYNC_SUSPEND", info, raw_state, STATE_BLOCKING_ASYNC_SUSPENDED, FALSE, 0, "");
  615. return TRUE; //Async suspend of blocking thread worked, now wait for resume
  616. /*
  617. STATE_RUNNING: A thread cannot escape suspension once requested.
  618. 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.
  619. 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.
  620. With cooperative suspend, there are no finish_async_suspend transitions since there's no path back from asyns_suspend requested to running.
  621. 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.
  622. */
  623. default:
  624. mono_fatal_with_history ("Cannot transition thread %p from %s with FINISH_ASYNC_SUSPEND", mono_thread_info_get_tid (info), state_name (cur_state));
  625. }
  626. }
  627. /*
  628. This transitions the thread into a cooperative state where it's assumed to be suspended but can continue.
  629. Native runtime code might want to put itself into a state where the thread is considered suspended but can keep running.
  630. That state only works as long as the only managed state touched is blitable and was pinned before the transition.
  631. It returns the action the caller must perform:
  632. - Continue: Entered blocking state sucessfully;
  633. - PollAndRetry: Async suspend raced and won, try to suspend and then retry;
  634. */
  635. MonoDoBlockingResult
  636. mono_threads_transition_do_blocking (MonoThreadInfo* info, const char *func)
  637. {
  638. int raw_state, cur_state, suspend_count;
  639. gboolean no_safepoints;
  640. retry_state_change:
  641. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  642. switch (cur_state) {
  643. case STATE_RUNNING: //transition to blocked
  644. if (!(suspend_count == 0))
  645. mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
  646. if (no_safepoints)
  647. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in state RUNNING with DO_BLOCKING");
  648. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING, suspend_count, no_safepoints), raw_state) != raw_state)
  649. goto retry_state_change;
  650. trace_state_change ("DO_BLOCKING", info, raw_state, STATE_BLOCKING, no_safepoints, 0);
  651. return DoBlockingContinue;
  652. case STATE_ASYNC_SUSPEND_REQUESTED:
  653. if (!(suspend_count > 0))
  654. mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
  655. if (no_safepoints)
  656. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in state ASYNC_SUSPEND_REQUESTED with DO_BLOCKING");
  657. trace_state_change ("DO_BLOCKING", info, raw_state, cur_state, no_safepoints, 0);
  658. return DoBlockingPollAndRetry;
  659. /*
  660. STATE_ASYNC_SUSPENDED
  661. STATE_SELF_SUSPENDED: Code should not be running while suspended.
  662. STATE_BLOCKING:
  663. STATE_BLOCKING_SUSPEND_REQUESTED:
  664. STATE_BLOCKING_SELF_SUSPENDED: Blocking is not nestabled
  665. STATE_BLOCKING_ASYNC_SUSPENDED: Blocking is not nestable _and_ code should not be running while suspended
  666. */
  667. default:
  668. 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));
  669. }
  670. }
  671. /*
  672. This is the exit transition from the blocking state. If this thread is logically async suspended it will have to wait
  673. until its resumed before continuing.
  674. It returns one of:
  675. -Ok: Done with blocking, just move on;
  676. -Wait: This thread was suspended while in blocking, wait for resume.
  677. */
  678. MonoDoneBlockingResult
  679. mono_threads_transition_done_blocking (MonoThreadInfo* info, const char *func)
  680. {
  681. int raw_state, cur_state, suspend_count;
  682. gboolean no_safepoints;
  683. retry_state_change:
  684. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  685. switch (cur_state) {
  686. case STATE_BLOCKING:
  687. if (!(suspend_count == 0))
  688. mono_fatal_with_history ("%s suspend_count = %d, but should be == 0", func, suspend_count);
  689. if (no_safepoints)
  690. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  691. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count, no_safepoints), raw_state) != raw_state)
  692. goto retry_state_change;
  693. trace_state_change_sigsafe ("DONE_BLOCKING", info, raw_state, STATE_RUNNING, no_safepoints, 0, func);
  694. return DoneBlockingOk;
  695. case STATE_BLOCKING_SUSPEND_REQUESTED:
  696. if (!(suspend_count > 0))
  697. mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
  698. if (no_safepoints)
  699. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  700. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SELF_SUSPENDED, suspend_count, no_safepoints), raw_state) != raw_state)
  701. goto retry_state_change;
  702. trace_state_change_with_func ("DONE_BLOCKING", info, raw_state, STATE_BLOCKING_SELF_SUSPENDED, no_safepoints, 0, func);
  703. return DoneBlockingWait;
  704. /*
  705. STATE_RUNNING: //Blocking was aborted and not properly restored
  706. STATE_ASYNC_SUSPEND_REQUESTED: //Blocking was aborted, not properly restored and now there's a pending suspend
  707. STATE_ASYNC_SUSPENDED
  708. STATE_SELF_SUSPENDED: Code should not be running while suspended.
  709. STATE_BLOCKING_SELF_SUSPENDED: This an exit state of done blocking
  710. STATE_BLOCKING_ASYNC_SUSPENDED: This is an exit state of done blocking
  711. */
  712. default:
  713. mono_fatal_with_history ("Cannot transition thread %p from %s with DONE_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
  714. }
  715. }
  716. /*
  717. Transition a thread in what should be a blocking state back to running state.
  718. This is different that done blocking because the goal is to get back to blocking once we're done.
  719. This is required to be able to bail out of blocking in case we're back to inside the runtime.
  720. It returns one of:
  721. -Ignore: Thread was not in blocking, nothing to do;
  722. -IgnoreAndPoll: Thread was not blocking and there's a pending suspend that needs to be processed;
  723. -Ok: Blocking state successfully aborted;
  724. -Wait: Blocking state successfully aborted, there's a pending suspend to be processed though, wait for resume.
  725. */
  726. MonoAbortBlockingResult
  727. mono_threads_transition_abort_blocking (THREAD_INFO_TYPE* info, const char *func)
  728. {
  729. int raw_state, cur_state, suspend_count;
  730. gboolean no_safepoints;
  731. retry_state_change:
  732. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  733. switch (cur_state) {
  734. case STATE_RUNNING: //thread already in runnable state
  735. /* Even though we're going to ignore this transition, still
  736. * assert about no_safepoints. Rationale: make it easier to catch
  737. * cases where we would be in ASYNC_SUSPEND_REQUESTED with
  738. * no_safepoints set, since those are polling points.
  739. */
  740. if (no_safepoints) {
  741. /* reset the state to no safepoints and then abort. If a
  742. * thread asserts somewhere because no_safepoints was set when it
  743. * shouldn't have been, we would get a second assertion here while
  744. * unwinding if we hadn't reset the no_safepoints flag.
  745. */
  746. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count, FALSE), raw_state) != raw_state)
  747. goto retry_state_change;
  748. /* record the current transition, in order to grab a backtrace */
  749. trace_state_change_with_func ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, FALSE, 0, func);
  750. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in state RUNNING with ABORT_BLOCKING");
  751. }
  752. trace_state_change_sigsafe ("ABORT_BLOCKING", info, raw_state, cur_state, no_safepoints, 0, func);
  753. return AbortBlockingIgnore;
  754. case STATE_ASYNC_SUSPEND_REQUESTED: //thread is runnable and have a pending suspend
  755. if (no_safepoints)
  756. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE in state ASYNC_SUSPEND_REQUESTED with ABORT_BLOCKING");
  757. trace_state_change_sigsafe ("ABORT_BLOCKING", info, raw_state, cur_state, no_safepoints, 0, func);
  758. return AbortBlockingIgnoreAndPoll;
  759. case STATE_BLOCKING:
  760. if (!(suspend_count == 0))
  761. mono_fatal_with_history ("suspend_count = %d, but should be == 0", suspend_count);
  762. if (no_safepoints)
  763. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  764. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_RUNNING, suspend_count, FALSE), raw_state) != raw_state)
  765. goto retry_state_change;
  766. trace_state_change_sigsafe ("ABORT_BLOCKING", info, raw_state, STATE_RUNNING, FALSE, 0, func);
  767. return AbortBlockingOk;
  768. case STATE_BLOCKING_SUSPEND_REQUESTED:
  769. if (!(suspend_count > 0))
  770. mono_fatal_with_history ("suspend_count = %d, but should be > 0", suspend_count);
  771. if (no_safepoints)
  772. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE");
  773. if (thread_state_cas (&info->thread_state, build_thread_state (STATE_BLOCKING_SELF_SUSPENDED, suspend_count, FALSE), raw_state) != raw_state)
  774. goto retry_state_change;
  775. trace_state_change_with_func ("ABORT_BLOCKING", info, raw_state, STATE_BLOCKING_SELF_SUSPENDED, FALSE, 0, func);
  776. return AbortBlockingWait;
  777. /*
  778. STATE_ASYNC_SUSPENDED:
  779. STATE_SELF_SUSPENDED: Code should not be running while suspended.
  780. STATE_BLOCKING_SELF_SUSPENDED: This is an exit state of done blocking, can't happen here.
  781. STATE_BLOCKING_ASYNC_SUSPENDED: This is an exit state of abort blocking, can't happen here.
  782. */
  783. default:
  784. mono_fatal_with_history ("Cannot transition thread %p from %s with ABORT_BLOCKING", mono_thread_info_get_tid (info), state_name (cur_state));
  785. }
  786. }
  787. /*
  788. Set the no_safepoints flag on an executing GC Unsafe thread.
  789. The no_safepoints bit prevents polling (hence self-suspending) and transitioning from GC Unsafe to GC Safe.
  790. Thus the thread will not be (cooperatively) interrupted while the bit is set.
  791. We don't allow nesting no_safepoints regions, so the flag must be initially unset.
  792. Since a suspend initiator may at any time request that a thread should suspend,
  793. ASYNC_SUSPEND_REQUESTED is allowed to have the no_safepoints bit set, too.
  794. (Future: We could augment this function to return a return value that tells the
  795. thread to poll and retry the transition since if we enter here in the
  796. ASYNC_SUSPEND_REQUESTED state).
  797. */
  798. void
  799. mono_threads_transition_begin_no_safepoints (MonoThreadInfo *info, const char *func)
  800. {
  801. int raw_state, cur_state, suspend_count;
  802. gboolean no_safepoints;
  803. retry_state_change:
  804. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  805. switch (cur_state) {
  806. case STATE_RUNNING:
  807. case STATE_ASYNC_SUSPEND_REQUESTED:
  808. /* Maybe revisit this. But for now, don't allow nesting. */
  809. if (no_safepoints)
  810. mono_fatal_with_history ("no_safepoints = TRUE, but should be FALSE with BEGIN_NO_SAFEPOINTS. Can't nest no safepointing regions");
  811. if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count, TRUE), raw_state) != raw_state)
  812. goto retry_state_change;
  813. trace_state_change_with_func ("BEGIN_NO_SAFEPOINTS", info, raw_state, cur_state, TRUE, 0, func);
  814. return;
  815. /*
  816. STATE_STARTING:
  817. STATE_DETACHED:
  818. STATE_SELF_SUSPENDED:
  819. STATE_ASYNC_SUSPENDED:
  820. STATE_BLOCKING:
  821. STATE_BLOCKING_ASYNC_SUSPENDED:
  822. STATE_BLOCKING_SELF_SUSPENDED:
  823. STATE_BLOCKING_SUSPEND_REQUESTED:
  824. no_safepoints only allowed for threads that are executing and GC Unsafe.
  825. */
  826. default:
  827. mono_fatal_with_history ("Cannot transition thread %p from %s with BEGIN_NO_SAFEPOINTS", mono_thread_info_get_tid (info), state_name (cur_state));
  828. }
  829. }
  830. /*
  831. Unset the no_safepoints flag on an executing GC Unsafe thread.
  832. The no_safepoints bit prevents polling (hence self-suspending) and transitioning from GC Unsafe to GC Safe.
  833. Thus the thread will not be (cooperatively) interrupted while the bit is set.
  834. We don't allow nesting no_safepoints regions, so the flag must be initially set.
  835. Since a suspend initiator may at any time request that a thread should suspend,
  836. ASYNC_SUSPEND_REQUESTED is allowed to have the no_safepoints bit set, too.
  837. (Future: We could augment this function to perform the transition and then
  838. return a return value that tells the thread to poll (and safepoint) if we enter
  839. here in the ASYNC_SUSPEND_REQUESTED state).
  840. */
  841. void
  842. mono_threads_transition_end_no_safepoints (MonoThreadInfo *info, const char *func)
  843. {
  844. int raw_state, cur_state, suspend_count;
  845. gboolean no_safepoints;
  846. retry_state_change:
  847. UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, no_safepoints, info);
  848. switch (cur_state) {
  849. case STATE_RUNNING:
  850. case STATE_ASYNC_SUSPEND_REQUESTED:
  851. if (!no_safepoints)
  852. mono_fatal_with_history ("no_safepoints = FALSE, but should be TRUE with END_NO_SAFEPOINTS. Unbalanced no safepointing region");
  853. if (thread_state_cas (&info->thread_state, build_thread_state (cur_state, suspend_count, FALSE), raw_state) != raw_state)
  854. goto retry_state_change;
  855. trace_state_change_with_func ("END_NO_SAFEPOINTS", info, raw_state, cur_state, FALSE, 0, func);
  856. return;
  857. /*
  858. STATE_STARTING:
  859. STATE_DETACHED:
  860. STATE_SELF_SUSPENDED:
  861. STATE_ASYNC_SUSPENDED:
  862. STATE_BLOCKING:
  863. STATE_BLOCKING_ASYNC_SUSPENDED:
  864. STATE_BLOCKING_SELF_SUSPENDED:
  865. STATE_BLOCKING_SUSPEND_REQUESTED:
  866. no_safepoints only allowed for threads that are executing and GC Unsafe.
  867. */
  868. default:
  869. mono_fatal_with_history ("Cannot transition thread %p from %s with END_NO_SAFEPOINTS", mono_thread_info_get_tid (info), state_name (cur_state));
  870. }
  871. }
  872. // State checking code
  873. /**
  874. * Return TRUE is the thread is in a runnable state.
  875. */
  876. gboolean
  877. mono_thread_info_is_running (MonoThreadInfo *info)
  878. {
  879. switch (mono_thread_info_current_state (info)) {
  880. case STATE_RUNNING:
  881. case STATE_ASYNC_SUSPEND_REQUESTED:
  882. case STATE_BLOCKING_SUSPEND_REQUESTED:
  883. case STATE_BLOCKING:
  884. return TRUE;
  885. }
  886. return FALSE;
  887. }
  888. /**
  889. * Return TRUE is the thread is in an usable (suspendable) state
  890. */
  891. gboolean
  892. mono_thread_info_is_live (MonoThreadInfo *info)
  893. {
  894. switch (mono_thread_info_current_state (info)) {
  895. case STATE_STARTING:
  896. case STATE_DETACHED:
  897. return FALSE;
  898. }
  899. return TRUE;
  900. }
  901. int
  902. mono_thread_info_suspend_count (MonoThreadInfo *info)
  903. {
  904. return info->thread_state.suspend_count;
  905. }
  906. int
  907. mono_thread_info_current_state (MonoThreadInfo *info)
  908. {
  909. return info->thread_state.state;
  910. }
  911. const char*
  912. mono_thread_state_name (int state)
  913. {
  914. return state_name (state);
  915. }
  916. gboolean
  917. mono_thread_is_gc_unsafe_mode (void)
  918. {
  919. MonoThreadInfo *cur = mono_thread_info_current ();
  920. if (!cur)
  921. return FALSE;
  922. switch (mono_thread_info_current_state (cur)) {
  923. case STATE_RUNNING:
  924. case STATE_ASYNC_SUSPEND_REQUESTED:
  925. return TRUE;
  926. default:
  927. return FALSE;
  928. }
  929. }
  930. gboolean
  931. mono_thread_info_will_not_safepoint (MonoThreadInfo *info)
  932. {
  933. return info->thread_state.no_safepoints;
  934. }