1 #include <mono/utils/mono-os-wait.h>
2 #include <mono/utils/mono-threads.h>
3 #include <mono/utils/mono-threads-debug.h>
5 #define THREAD_WAIT_INFO_CLEARED 0
6 #define THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT 1
7 #define THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT 2
8 #define THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT 4
11 reqeust_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid)
14 * On Windows platforms the async interrupt/abort requests might need to process
15 * APC on target thread before the thread can return back from OS wait calls
16 * and complete the mono interrupt/abort request. In such cases, just keep on
17 * queuing APC over again again will flood the APC queue preventing the target thread
18 * to return from it's alertable OS wait call. This check makes sure not to issue an APC
19 * as long as there is a pending requests in requested APC slot for the targeted thread.
20 * NOTE, this code will executed regardless if thread is currently in an alertable wait or not.
21 * This is done to prevent races between interrupt/abort occurring just before thread enters an
22 * alertable wait. Threads entering waits already need to handle WAIT_IO_COMPLETION scenarios and
23 * if that happens due to a previous pending interrupt/abort APC, the calling thread should already
24 * have logic to restart the alertable wait operation.
26 MonoThreadInfo *info = (MonoThreadInfo *)thread_info;
27 gboolean queue_apc = FALSE;
30 gint32 old_wait_info = InterlockedRead (&info->thread_wait_info);
31 if (old_wait_info & pending_apc_slot)
34 gint32 new_wait_info = old_wait_info | pending_apc_slot;
35 if (InterlockedCompareExchange (&info->thread_wait_info, new_wait_info, old_wait_info) == old_wait_info) {
40 if (queue_apc == TRUE) {
41 THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid);
42 QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL);
47 interrupt_apc (ULONG_PTR param)
49 THREADS_INTERRUPT_DEBUG ("%06d - interrupt_apc () called", GetCurrentThreadId ());
53 mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
55 reqeust_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT, interrupt_apc, tid);
59 abort_apc (ULONG_PTR param)
61 THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
65 mono_win32_abort_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
67 reqeust_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT, abort_apc, tid);
71 enter_alertable_wait (MonoThreadInfo *info)
73 // Clear any previous flags. Set alertable wait flag.
74 InterlockedExchange (&info->thread_wait_info, THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT);
78 leave_alertable_wait (MonoThreadInfo *info)
80 // Clear any previous flags. Thread is exiting alertable wait state, and info around pending interrupt/abort APC's
81 // can now be discarded as well, thread is out of wait operation and can proceed it's execution.
82 InterlockedExchange (&info->thread_wait_info, THREAD_WAIT_INFO_CLEARED);
86 mono_win32_sleep_ex (DWORD timeout, BOOL alertable)
88 DWORD result = WAIT_FAILED;
89 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
91 if (alertable && info) {
92 enter_alertable_wait (info);
95 result = SleepEx (timeout, alertable);
97 // NOTE, leave_alertable_wait should not affect GetLastError but
98 // if changed, last error need to be preserved and reset before returning.
99 if (alertable && info) {
100 leave_alertable_wait (info);
107 mono_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertable)
109 DWORD result = WAIT_FAILED;
110 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
112 if (alertable && info) {
113 enter_alertable_wait (info);
116 result = WaitForSingleObjectEx (handle, timeout, alertable);
118 // NOTE, leave_alertable_wait should not affect GetLastError but
119 // if changed, last error need to be preserved and reset before returning.
120 if (alertable && info) {
121 leave_alertable_wait (info);
128 mono_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
130 DWORD result = WAIT_FAILED;
131 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
133 if (alertable && info) {
134 enter_alertable_wait (info);
137 result = WaitForMultipleObjectsEx (count, handles, waitAll, timeout, alertable);
139 // NOTE, leave_alertable_wait should not affect GetLastError but
140 // if changed, last error need to be preserved and reset before returning.
141 if (alertable && info) {
142 leave_alertable_wait (info);
149 mono_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable)
151 DWORD result = WAIT_FAILED;
152 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
154 if (alertable && info) {
155 enter_alertable_wait (info);
158 result = SignalObjectAndWait (toSignal, towlower, timeout, alertable);
160 // NOTE, leave_alertable_wait should not affect GetLastError but
161 // if changed, last error need to be preserved and reset before returning.
162 if (alertable && info) {
163 leave_alertable_wait (info);
170 mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags)
172 DWORD result = WAIT_FAILED;
173 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
174 BOOL alertable = flags & MWMO_ALERTABLE;
176 if (alertable && info) {
177 enter_alertable_wait (info);
180 result = MsgWaitForMultipleObjectsEx (count, handles, timeout, wakeMask, flags);
182 // NOTE, leave_alertable_wait should not affect GetLastError but
183 // if changed, last error need to be preserved and reset before returning.
184 if (alertable && info) {
185 leave_alertable_wait (info);
192 mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
194 DWORD result = WAIT_FAILED;
195 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
197 if (alertable && info) {
198 enter_alertable_wait (info);
201 result = WSAWaitForMultipleEvents (count, handles, waitAll, timeout, alertable);
203 // NOTE, leave_alertable_wait should not affect GetLastError but
204 // if changed, last error need to be preserved and reset before returning.
205 if (alertable && info) {
206 leave_alertable_wait (info);