3 * Win32 OS wait wrappers and interrupt/abort APC handling.
6 * Johan Lorensson (lateralusx.github@gmail.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11 #include <mono/utils/mono-os-wait.h>
12 #include <mono/utils/mono-threads.h>
13 #include <mono/utils/mono-threads-debug.h>
15 #define THREAD_WAIT_INFO_CLEARED 0
16 #define THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT 1
17 #define THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT 2
18 #define THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT 4
21 reqeust_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid)
24 * On Windows platforms, an async interrupt/abort request queues an APC
25 * that needs to be processed by target thread before it can return from an
26 * alertable OS wait call and complete the mono interrupt/abort request.
27 * Uncontrolled queuing of APC's could flood the APC queue preventing the target thread
28 * to return from its alertable OS wait call, blocking the interrupt/abort requests to complete
29 * This check makes sure that only one APC per type gets queued, preventing potential flooding
30 * of the APC queue. NOTE, this code will execute regardless if targeted thread is currently in
31 * an alertable wait or not. This is done to prevent races between interrupt/abort requests and
32 * alertable wait calls. Threads already in an alertable wait should handle WAIT_IO_COMPLETION
33 * return scenarios and restart the alertable wait operation if needed or take other actions
34 * (like service the interrupt/abort request).
36 MonoThreadInfo *info = (MonoThreadInfo *)thread_info;
37 gboolean queue_apc = FALSE;
40 gint32 old_wait_info = InterlockedRead (&info->thread_wait_info);
41 if (old_wait_info & pending_apc_slot)
44 gint32 new_wait_info = old_wait_info | pending_apc_slot;
45 if (InterlockedCompareExchange (&info->thread_wait_info, new_wait_info, old_wait_info) == old_wait_info) {
50 if (queue_apc == TRUE) {
51 THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid);
52 QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL);
57 interrupt_apc (ULONG_PTR param)
59 THREADS_INTERRUPT_DEBUG ("%06d - interrupt_apc () called", GetCurrentThreadId ());
63 mono_win32_interrupt_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
65 reqeust_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT, interrupt_apc, tid);
69 abort_apc (ULONG_PTR param)
71 THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
75 mono_win32_abort_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
77 reqeust_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT, abort_apc, tid);
81 enter_alertable_wait (MonoThreadInfo *info)
83 // Clear any previous flags. Set alertable wait flag.
84 InterlockedExchange (&info->thread_wait_info, THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT);
88 leave_alertable_wait (MonoThreadInfo *info)
90 // Clear any previous flags. Thread is exiting alertable wait state, and info around pending interrupt/abort APC's
91 // can now be discarded as well, thread is out of wait operation and can proceed it's execution.
92 InterlockedExchange (&info->thread_wait_info, THREAD_WAIT_INFO_CLEARED);
96 mono_win32_sleep_ex (DWORD timeout, BOOL alertable)
98 DWORD result = WAIT_FAILED;
99 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
101 if (alertable && info) {
102 enter_alertable_wait (info);
105 result = SleepEx (timeout, alertable);
107 // NOTE, leave_alertable_wait should not affect GetLastError but
108 // if changed, GetLastError needs to be preserved and reset before returning.
109 if (alertable && info) {
110 leave_alertable_wait (info);
117 mono_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertable)
119 DWORD result = WAIT_FAILED;
120 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
122 if (alertable && info) {
123 enter_alertable_wait (info);
126 result = WaitForSingleObjectEx (handle, timeout, alertable);
128 // NOTE, leave_alertable_wait should not affect GetLastError but
129 // if changed, GetLastError needs to be preserved and reset before returning.
130 if (alertable && info) {
131 leave_alertable_wait (info);
138 mono_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
140 DWORD result = WAIT_FAILED;
141 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
143 if (alertable && info) {
144 enter_alertable_wait (info);
147 result = WaitForMultipleObjectsEx (count, handles, waitAll, timeout, alertable);
149 // NOTE, leave_alertable_wait should not affect GetLastError but
150 // if changed, GetLastError needs to be preserved and reset before returning.
151 if (alertable && info) {
152 leave_alertable_wait (info);
159 mono_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable)
161 DWORD result = WAIT_FAILED;
162 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
164 if (alertable && info) {
165 enter_alertable_wait (info);
168 result = SignalObjectAndWait (toSignal, toWait, timeout, alertable);
170 // NOTE, leave_alertable_wait should not affect GetLastError but
171 // if changed, GetLastError needs to be preserved and reset before returning.
172 if (alertable && info) {
173 leave_alertable_wait (info);
180 mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags)
182 DWORD result = WAIT_FAILED;
183 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
184 BOOL alertable = flags & MWMO_ALERTABLE;
186 if (alertable && info) {
187 enter_alertable_wait (info);
190 result = MsgWaitForMultipleObjectsEx (count, handles, timeout, wakeMask, flags);
192 // NOTE, leave_alertable_wait should not affect GetLastError but
193 // if changed, GetLastError needs to be preserved and reset before returning.
194 if (alertable && info) {
195 leave_alertable_wait (info);
202 mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
204 DWORD result = WAIT_FAILED;
205 MonoThreadInfo *info = mono_thread_info_current_unchecked ();
207 if (alertable && info) {
208 enter_alertable_wait (info);
211 result = WSAWaitForMultipleEvents (count, handles, waitAll, timeout, alertable);
213 // NOTE, leave_alertable_wait should not affect GetLastError but
214 // if changed, GetLastError needs to be preserved and reset before returning.
215 if (alertable && info) {
216 leave_alertable_wait (info);