mono-threads-windows.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. /**
  2. * \file
  3. * Low-level threading, windows version
  4. *
  5. * Author:
  6. * Rodrigo Kumpera (kumpera@gmail.com)
  7. *
  8. * (C) 2011 Novell, Inc
  9. */
  10. #include <mono/utils/mono-threads.h>
  11. #if defined(USE_WINDOWS_BACKEND)
  12. #include <glib.h>
  13. #include <mono/utils/mono-compiler.h>
  14. #include <mono/utils/mono-threads-coop.h>
  15. #include <mono/utils/mono-threads-debug.h>
  16. #include <mono/utils/mono-os-wait.h>
  17. #include <mono/utils/mono-context.h>
  18. #include <mono/utils/w32subset.h>
  19. #include <limits.h>
  20. enum Win32APCInfo {
  21. WIN32_APC_INFO_CLEARED = 0,
  22. WIN32_APC_INFO_ALERTABLE_WAIT_SLOT = 1 << 0,
  23. WIN32_APC_INFO_BLOCKING_IO_SLOT = 1 << 1,
  24. WIN32_APC_INFO_PENDING_INTERRUPT_SLOT = 1 << 2,
  25. WIN32_APC_INFO_PENDING_ABORT_SLOT = 1 << 3
  26. };
  27. static void
  28. request_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid)
  29. {
  30. /*
  31. * On Windows platforms, an async interrupt/abort request queues an APC
  32. * that needs to be processed by target thread before it can return from an
  33. * alertable OS wait call and complete the mono interrupt/abort request.
  34. * Uncontrolled queuing of APC's could flood the APC queue preventing the target thread
  35. * to return from its alertable OS wait call, blocking the interrupt/abort requests to complete.
  36. * This check makes sure that only one APC per type gets queued, preventing potential flooding
  37. * of the APC queue. NOTE, this code will execute regardless if targeted thread is currently in
  38. * an alertable wait or not. This is done to prevent races between interrupt/abort requests and
  39. * alertable wait calls. Threads already in an alertable wait should handle WAIT_IO_COMPLETION
  40. * return scenarios and restart the alertable wait operation if needed or take other actions
  41. * (like service the interrupt/abort request).
  42. */
  43. MonoThreadInfo *info = (MonoThreadInfo *)thread_info;
  44. gint32 old_apc_info, new_apc_info;
  45. do {
  46. old_apc_info = mono_atomic_load_i32 (&info->win32_apc_info);
  47. if (old_apc_info & pending_apc_slot)
  48. return;
  49. new_apc_info = old_apc_info | pending_apc_slot;
  50. } while (mono_atomic_cas_i32 (&info->win32_apc_info, new_apc_info, old_apc_info) != old_apc_info);
  51. THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid);
  52. QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL);
  53. }
  54. static void CALLBACK
  55. interrupt_apc (ULONG_PTR param)
  56. {
  57. THREADS_INTERRUPT_DEBUG ("%06d - interrupt_apc () called", GetCurrentThreadId ());
  58. }
  59. void
  60. mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
  61. {
  62. request_interrupt (thread_info, native_thread_handle, WIN32_APC_INFO_PENDING_INTERRUPT_SLOT, interrupt_apc, tid);
  63. }
  64. static void CALLBACK
  65. abort_apc (ULONG_PTR param)
  66. {
  67. THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
  68. #if HAVE_API_SUPPORT_WIN32_CANCEL_IO || HAVE_API_SUPPORT_WIN32_CANCEL_IO_EX
  69. MonoThreadInfo *info = mono_thread_info_current_unchecked ();
  70. if (info) {
  71. // Check if pending interrupt is still relevant and current thread has not left alertable wait region.
  72. // NOTE, can only be reset by current thread, currently running this APC.
  73. gint32 win32_apc_info = mono_atomic_load_i32 (&info->win32_apc_info);
  74. if (win32_apc_info & WIN32_APC_INFO_BLOCKING_IO_SLOT) {
  75. // Check if current thread registered an IO handle when entering alertable wait (blocking IO call).
  76. // No need for CAS on win32_apc_info_io_handle since its only loaded/stored by current thread
  77. // currently running APC.
  78. HANDLE io_handle = (HANDLE)info->win32_apc_info_io_handle;
  79. if (io_handle != INVALID_HANDLE_VALUE) {
  80. // In order to break IO waits, cancel all outstanding IO requests.
  81. // NOTE, this is NOT a blocking call.
  82. #if HAVE_API_SUPPORT_WIN32_CANCEL_IO
  83. // Start to cancel IO requests for the registered IO handle issued by current thread.
  84. CancelIo (io_handle);
  85. #elif HAVE_API_SUPPORT_WIN32_CANCEL_IO_EX
  86. CancelIoEx (io_handle, NULL);
  87. #endif
  88. }
  89. }
  90. }
  91. #endif /* HAVE_API_SUPPORT_WIN32_CANCEL_IO || HAVE_API_SUPPORT_WIN32_CANCEL_IO_EX */
  92. }
  93. // Attempt to cancel sync blocking IO on abort syscall requests.
  94. // NOTE, the effect of the canceled IO operation is unknown so the caller need
  95. // to close used resources (file, socket) to get back to a known state. The need
  96. // to abort blocking IO calls is normally part of doing a thread abort, then the
  97. // thread is going away meaning that no more IO calls will be issued against the
  98. // same resource that was part of the cancelation. Current implementation of
  99. // .NET Framework and .NET Core currently don't support the ability to abort a thread
  100. // blocked on sync IO calls, see https://github.com/dotnet/runtime/issues/16236.
  101. // Since there is no solution covering all scenarios aborting blocking syscall this
  102. // will be on best effort and there might still be a slight risk that the blocking call
  103. // won't abort (depending on overlapped IO support for current file, socket).
  104. static void
  105. suspend_abort_syscall (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
  106. {
  107. request_interrupt (thread_info, native_thread_handle, WIN32_APC_INFO_PENDING_ABORT_SLOT, abort_apc, tid);
  108. }
  109. static void
  110. enter_alertable_wait_ex (MonoThreadInfo *info, HANDLE io_handle)
  111. {
  112. // Only loaded/stored by current thread, here or in APC (also running on current thread).
  113. g_assert (info->win32_apc_info_io_handle == (gpointer)INVALID_HANDLE_VALUE);
  114. info->win32_apc_info_io_handle = io_handle;
  115. //Set alertable wait flag.
  116. mono_atomic_xchg_i32 (&info->win32_apc_info, (io_handle == INVALID_HANDLE_VALUE) ? WIN32_APC_INFO_ALERTABLE_WAIT_SLOT : WIN32_APC_INFO_BLOCKING_IO_SLOT);
  117. }
  118. static void
  119. leave_alertable_wait_ex (MonoThreadInfo *info, HANDLE io_handle)
  120. {
  121. // Clear any previous flags. Thread is exiting alertable wait region, and info around pending interrupt/abort APC's
  122. // can now be discarded, thread is out of wait operation and can proceed execution.
  123. mono_atomic_xchg_i32 (&info->win32_apc_info, WIN32_APC_INFO_CLEARED);
  124. // Only loaded/stored by current thread, here or in APC (also running on current thread).
  125. g_assert (info->win32_apc_info_io_handle == io_handle);
  126. info->win32_apc_info_io_handle = (gpointer)INVALID_HANDLE_VALUE;
  127. }
  128. void
  129. mono_win32_enter_alertable_wait (THREAD_INFO_TYPE *info)
  130. {
  131. if (info)
  132. enter_alertable_wait_ex (info, INVALID_HANDLE_VALUE);
  133. }
  134. void
  135. mono_win32_leave_alertable_wait (THREAD_INFO_TYPE *info)
  136. {
  137. if (info)
  138. leave_alertable_wait_ex (info, INVALID_HANDLE_VALUE);
  139. }
  140. void
  141. mono_win32_enter_blocking_io_call (THREAD_INFO_TYPE *info, HANDLE io_handle)
  142. {
  143. if (info)
  144. enter_alertable_wait_ex (info, io_handle);
  145. }
  146. void
  147. mono_win32_leave_blocking_io_call (THREAD_INFO_TYPE *info, HANDLE io_handle)
  148. {
  149. if (info)
  150. leave_alertable_wait_ex (info, io_handle);
  151. }
  152. void
  153. mono_threads_suspend_init (void)
  154. {
  155. }
  156. gboolean
  157. mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
  158. {
  159. DWORD id = mono_thread_info_get_tid (info);
  160. HANDLE handle;
  161. DWORD result;
  162. handle = info->native_handle;
  163. g_assert (handle);
  164. result = SuspendThread (handle);
  165. THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %u\n", GUINT_TO_POINTER (id), result);
  166. if (result == (DWORD)-1) {
  167. if (!mono_threads_transition_abort_async_suspend (info)) {
  168. /* We raced with self suspend and lost so suspend can continue. */
  169. g_assert (mono_threads_is_hybrid_suspension_enabled ());
  170. info->suspend_can_continue = TRUE;
  171. THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (info));
  172. return TRUE;
  173. }
  174. THREADS_SUSPEND_DEBUG ("SUSPEND FAILED, id=%p, err=%u\n", GUINT_TO_POINTER (id), GetLastError ());
  175. return FALSE;
  176. }
  177. /* Suspend logic assumes thread is really suspended before continuing below. Surprisingly SuspendThread */
  178. /* is just an async request to the scheduler, meaning that the suspended thread can continue to run */
  179. /* user mode code until scheduler gets around and process the request. This will cause a thread state race */
  180. /* in mono's thread state machine implementation on Windows. By requesting a threads context after issuing a */
  181. /* suspended request, this will wait until thread is suspended and thread context has been collected */
  182. /* and returned. */
  183. #if defined(MONO_HAVE_SIMD_REG_AVX) && HAVE_API_SUPPORT_WIN32_CONTEXT_XSTATE
  184. BYTE context_buffer [2048];
  185. DWORD context_buffer_len = G_N_ELEMENTS (context_buffer);
  186. PCONTEXT context = NULL;
  187. BOOL success = InitializeContext (context_buffer, CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL | CONTEXT_XSTATE, &context, &context_buffer_len);
  188. success &= SetXStateFeaturesMask (context, XSTATE_MASK_AVX);
  189. g_assert (success == TRUE);
  190. #else
  191. CONTEXT context_buffer;
  192. PCONTEXT context = &context_buffer;
  193. context->ContextFlags = CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL;
  194. #endif
  195. if (!GetThreadContext (handle, context)) {
  196. result = ResumeThread (handle);
  197. g_assert (result == 1);
  198. if (!mono_threads_transition_abort_async_suspend (info)) {
  199. /* We raced with self suspend and lost so suspend can continue. */
  200. g_assert (mono_threads_is_hybrid_suspension_enabled ());
  201. info->suspend_can_continue = TRUE;
  202. THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (info));
  203. return TRUE;
  204. }
  205. THREADS_SUSPEND_DEBUG ("SUSPEND FAILED (GetThreadContext), id=%p, err=%u\n", GUINT_TO_POINTER (id), GetLastError ());
  206. return FALSE;
  207. }
  208. if (!mono_threads_transition_finish_async_suspend (info)) {
  209. /* We raced with self-suspend and lost. Resume the native
  210. * thread. It is still self-suspended, waiting to be resumed.
  211. * So suspend can continue.
  212. */
  213. result = ResumeThread (handle);
  214. g_assert (result == 1);
  215. info->suspend_can_continue = TRUE;
  216. THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", GUINT_TO_POINTER (id));
  217. g_assert (mono_threads_is_hybrid_suspension_enabled ());
  218. //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
  219. return TRUE;
  220. }
  221. info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info, context);
  222. THREADS_SUSPEND_DEBUG ("thread state %p -> %u\n", GUINT_TO_POINTER (id), result);
  223. if (info->suspend_can_continue) {
  224. if (interrupt_kernel)
  225. suspend_abort_syscall (info, handle, id);
  226. } else {
  227. THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %u\n", GUINT_TO_POINTER (id), 0);
  228. }
  229. return TRUE;
  230. }
  231. gboolean
  232. mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
  233. {
  234. return info->suspend_can_continue;
  235. }
  236. void
  237. mono_threads_suspend_abort_syscall (MonoThreadInfo *info)
  238. {
  239. DWORD id = mono_thread_info_get_tid(info);
  240. g_assert (info->native_handle);
  241. suspend_abort_syscall (info, info->native_handle, id);
  242. }
  243. void
  244. mono_win32_abort_blocking_io_call (MonoThreadInfo *info)
  245. {
  246. #if HAVE_API_SUPPORT_WIN32_CANCEL_SYNCHRONOUS_IO
  247. // In case thread is blocked on sync IO preventing it from running above queued APC, cancel
  248. // all outputstanding sync IO for target thread. If its not blocked on a sync IO request, below
  249. // call will just fail and nothing will be canceled. If thread is waiting on overlapped IO,
  250. // the queued APC will take care of cancel specific outstanding IO requests.
  251. gint32 win32_apc_info = mono_atomic_load_i32 (&info->win32_apc_info);
  252. if (win32_apc_info & WIN32_APC_INFO_BLOCKING_IO_SLOT) {
  253. CancelSynchronousIo (info->native_handle);
  254. }
  255. #endif
  256. }
  257. gboolean
  258. mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
  259. {
  260. HANDLE handle;
  261. DWORD result;
  262. handle = info->native_handle;
  263. g_assert (handle);
  264. if (info->async_target) {
  265. #if HAVE_API_SUPPORT_WIN32_SET_THREAD_CONTEXT
  266. MonoContext ctx;
  267. CONTEXT context;
  268. gboolean res;
  269. ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
  270. mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
  271. info->async_target = NULL;
  272. info->user_data = NULL;
  273. // When using MONO_HAVE_SIMD_REG_AVX, Mono won't change YMM (read only), so no need to
  274. // read extended context state.
  275. context.ContextFlags = CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL;
  276. if (!GetThreadContext (handle, &context)) {
  277. THREADS_SUSPEND_DEBUG ("RESUME FAILED (GetThreadContext), id=%p, err=%u\n", GUINT_TO_POINTER (mono_thread_info_get_tid (info)), GetLastError ());
  278. return FALSE;
  279. }
  280. mono_monoctx_to_sigctx (&ctx, &context);
  281. // When using MONO_HAVE_SIMD_REG_AVX, Mono won't change YMM (read only), so no need to
  282. // write extended context state.
  283. context.ContextFlags = CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_CONTROL;
  284. res = SetThreadContext (handle, &context);
  285. if (!res) {
  286. THREADS_SUSPEND_DEBUG ("RESUME FAILED (SetThreadContext), id=%p, err=%u\n", GUINT_TO_POINTER (mono_thread_info_get_tid (info)), GetLastError ());
  287. return FALSE;
  288. }
  289. #else
  290. g_error ("Not implemented due to lack of SetThreadContext");
  291. #endif
  292. }
  293. result = ResumeThread (handle);
  294. THREADS_SUSPEND_DEBUG ("RESUME %p -> %u\n", GUINT_TO_POINTER (mono_thread_info_get_tid (info)), result);
  295. return result != (DWORD)-1;
  296. }
  297. void
  298. mono_threads_suspend_register (MonoThreadInfo *info)
  299. {
  300. g_assert (!info->native_handle);
  301. info->native_handle = mono_threads_open_native_thread_handle (GetCurrentThread ());
  302. }
  303. void
  304. mono_threads_suspend_free (MonoThreadInfo *info)
  305. {
  306. mono_threads_close_native_thread_handle (info->native_handle);
  307. info->native_handle = NULL;
  308. }
  309. void
  310. mono_threads_suspend_init_signals (void)
  311. {
  312. }
  313. gint
  314. mono_threads_suspend_search_alternative_signal (void)
  315. {
  316. g_assert_not_reached ();
  317. }
  318. gint
  319. mono_threads_suspend_get_suspend_signal (void)
  320. {
  321. return -1;
  322. }
  323. gint
  324. mono_threads_suspend_get_restart_signal (void)
  325. {
  326. return -1;
  327. }
  328. gint
  329. mono_threads_suspend_get_abort_signal (void)
  330. {
  331. return -1;
  332. }
  333. #endif
  334. #if defined (HOST_WIN32)
  335. #ifndef ENABLE_NETCORE
  336. #define MONO_WIN32_DEFAULT_NATIVE_STACK_SIZE (1024 * 1024)
  337. #else
  338. // Use default stack size on netcore.
  339. #define MONO_WIN32_DEFAULT_NATIVE_STACK_SIZE 0
  340. #endif
  341. gboolean
  342. mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid)
  343. {
  344. HANDLE result;
  345. DWORD thread_id;
  346. gsize set_stack_size = MONO_WIN32_DEFAULT_NATIVE_STACK_SIZE;
  347. if (stack_size && *stack_size)
  348. set_stack_size = *stack_size;
  349. result = CreateThread (NULL, set_stack_size, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
  350. if (!result)
  351. return FALSE;
  352. /* A new handle is open when attaching
  353. * the thread, so we don't need this one */
  354. CloseHandle (result);
  355. if (tid)
  356. *tid = thread_id;
  357. if (stack_size) {
  358. // TOOD: Use VirtualQuery to get correct value
  359. // http://stackoverflow.com/questions/2480095/thread-stack-size-on-windows-visual-c
  360. *stack_size = set_stack_size;
  361. }
  362. return TRUE;
  363. }
  364. MonoNativeThreadId
  365. mono_native_thread_id_get (void)
  366. {
  367. return GetCurrentThreadId ();
  368. }
  369. guint64
  370. mono_native_thread_os_id_get (void)
  371. {
  372. return (guint64)GetCurrentThreadId ();
  373. }
  374. gint32
  375. mono_native_thread_processor_id_get (void)
  376. {
  377. PROCESSOR_NUMBER proc_num;
  378. GetCurrentProcessorNumberEx (&proc_num);
  379. return ((proc_num.Group << 6) | proc_num.Number);
  380. }
  381. gboolean
  382. mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
  383. {
  384. return id1 == id2;
  385. }
  386. gboolean
  387. mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
  388. {
  389. return CreateThread (NULL, MONO_WIN32_DEFAULT_NATIVE_STACK_SIZE, (LPTHREAD_START_ROUTINE)func, arg, 0, tid) != NULL;
  390. }
  391. gboolean
  392. mono_native_thread_join_handle (HANDLE thread_handle, gboolean close_handle)
  393. {
  394. DWORD res = WaitForSingleObject (thread_handle, INFINITE);
  395. if (close_handle)
  396. CloseHandle (thread_handle);
  397. return res != WAIT_FAILED;
  398. }
  399. #if HAVE_API_SUPPORT_WIN32_OPEN_THREAD
  400. gboolean
  401. mono_native_thread_join (MonoNativeThreadId tid)
  402. {
  403. HANDLE handle;
  404. if (!(handle = OpenThread (SYNCHRONIZE, TRUE, tid)))
  405. return FALSE;
  406. return mono_native_thread_join_handle (handle, TRUE);
  407. }
  408. #elif !HAVE_EXTERN_DEFINED_WIN32_OPEN_THREAD
  409. gboolean
  410. mono_native_thread_join (MonoNativeThreadId tid)
  411. {
  412. g_unsupported_api ("OpenThread");
  413. SetLastError (ERROR_NOT_SUPPORTED);
  414. return FALSE;
  415. }
  416. #endif
  417. void
  418. mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
  419. {
  420. #if _WIN32_WINNT >= 0x0602 // Windows 8 or newer and very fast, just a few instructions, no syscall.
  421. ULONG_PTR low;
  422. ULONG_PTR high;
  423. GetCurrentThreadStackLimits (&low, &high);
  424. *staddr = (guint8*)low;
  425. *stsize = high - low;
  426. #else // Win7 and older (or newer, still works, but much slower).
  427. MEMORY_BASIC_INFORMATION info;
  428. // Windows stacks are commited on demand, one page at time.
  429. // teb->StackBase is the top from which it grows down.
  430. // teb->StackLimit is commited, the lowest it has gone so far.
  431. // info.AllocationBase is reserved, the lowest it can go.
  432. //
  433. VirtualQuery (&info, &info, sizeof (info));
  434. *staddr = (guint8*)info.AllocationBase;
  435. // TEB starts with TIB. TIB is public, TEB is not.
  436. *stsize = (size_t)((NT_TIB*)NtCurrentTeb ())->StackBase - (size_t)info.AllocationBase;
  437. #endif
  438. }
  439. #if SIZEOF_VOID_P == 4 && HAVE_API_SUPPORT_WIN32_IS_WOW64_PROCESS
  440. typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);
  441. static gboolean is_wow64 = FALSE;
  442. #endif
  443. /* We do this at init time to avoid potential races with module opening */
  444. void
  445. mono_threads_platform_init (void)
  446. {
  447. #if SIZEOF_VOID_P == 4 && HAVE_API_SUPPORT_WIN32_IS_WOW64_PROCESS
  448. LPFN_ISWOW64PROCESS is_wow64_func = (LPFN_ISWOW64PROCESS) GetProcAddress (GetModuleHandle (TEXT ("kernel32")), "IsWow64Process");
  449. if (is_wow64_func)
  450. is_wow64_func (GetCurrentProcess (), &is_wow64);
  451. #endif
  452. }
  453. static gboolean
  454. thread_is_cooperative_suspend_aware (MonoThreadInfo *info)
  455. {
  456. return (mono_threads_is_cooperative_suspension_enabled () || mono_atomic_load_i32 (&(info->coop_aware_thread)));
  457. }
  458. /*
  459. * When running x86 process under x64 system syscalls are done through WoW64. This
  460. * needs to do a transition from x86 mode to x64 so it can syscall into the x64 system.
  461. * Apparently this transition invalidates the ESP that we would get from calling
  462. * GetThreadContext, so we would fail to scan parts of the thread stack. We attempt
  463. * to query whether the thread is in such a transition so we try to restart it later.
  464. * We check CONTEXT_EXCEPTION_ACTIVE for this, which is highly undocumented.
  465. */
  466. gboolean
  467. mono_threads_platform_in_critical_region (THREAD_INFO_TYPE *info)
  468. {
  469. gboolean ret = FALSE;
  470. #if SIZEOF_VOID_P == 4 && HAVE_API_SUPPORT_WIN32_IS_WOW64_PROCESS && HAVE_API_SUPPORT_WIN32_OPEN_THREAD
  471. /* FIXME On cygwin these are not defined */
  472. #if defined(CONTEXT_EXCEPTION_REQUEST) && defined(CONTEXT_EXCEPTION_REPORTING) && defined(CONTEXT_EXCEPTION_ACTIVE)
  473. if (is_wow64 && thread_is_cooperative_suspend_aware (info)) {
  474. /* Cooperative suspended threads will block at well-defined locations. */
  475. return FALSE;
  476. } else if (is_wow64 && mono_threads_is_hybrid_suspension_enabled ()) {
  477. /* If thread is cooperative suspended, we shouldn't validate context */
  478. /* since thread could still be running towards it's wait state. Calling */
  479. /* GetThreadContext on a running thread (before calling SuspendThread) */
  480. /* could return incorrect results and since cooperative suspended threads */
  481. /* will block at well defined-locations, no need to do so. */
  482. int thread_state = mono_thread_info_current_state (info);
  483. if (thread_state == STATE_SELF_SUSPENDED || thread_state == STATE_BLOCKING_SELF_SUSPENDED)
  484. return FALSE;
  485. }
  486. if (is_wow64) {
  487. HANDLE handle = OpenThread (THREAD_ALL_ACCESS, FALSE, mono_thread_info_get_tid (info));
  488. if (handle) {
  489. CONTEXT context;
  490. ZeroMemory (&context, sizeof (CONTEXT));
  491. context.ContextFlags = CONTEXT_EXCEPTION_REQUEST;
  492. if (GetThreadContext (handle, &context)) {
  493. if ((context.ContextFlags & CONTEXT_EXCEPTION_REPORTING) &&
  494. (context.ContextFlags & CONTEXT_EXCEPTION_ACTIVE))
  495. ret = TRUE;
  496. }
  497. CloseHandle (handle);
  498. }
  499. }
  500. #endif
  501. #endif
  502. return ret;
  503. }
  504. gboolean
  505. mono_threads_platform_yield (void)
  506. {
  507. return SwitchToThread ();
  508. }
  509. void
  510. mono_threads_platform_exit (gsize exit_code)
  511. {
  512. ExitThread (exit_code);
  513. }
  514. int
  515. mono_thread_info_get_system_max_stack_size (void)
  516. {
  517. //FIXME
  518. return INT_MAX;
  519. }
  520. void
  521. mono_memory_barrier_process_wide (void)
  522. {
  523. FlushProcessWriteBuffers ();
  524. }
  525. #else
  526. #include <mono/utils/mono-compiler.h>
  527. MONO_EMPTY_SOURCE_FILE (mono_threads_windows);
  528. #endif