undo change in mini.c
[mono-project.git] / mono / utils / mono-os-wait-win32.c
blob4bd9035a3419c9f7be8b349f37e1bc052a64ac7c
1 /**
2 * \file
3 * Win32 OS wait wrappers and interrupt/abort APC handling.
5 * Author:
6 * Johan Lorensson (lateralusx.github@gmail.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9 */
11 #include "mono-os-wait.h"
12 #include "mono-threads.h"
13 #include "mono-threads-debug.h"
14 #include "mono-logger-internals.h"
15 #include "mono-error-internals.h"
16 #include <mono/metadata/w32subset.h>
17 #include <mono/utils/checked-build.h>
19 /* Empty handler only used to detect interrupt state of current thread. */
20 /* Needed in order to correctly avoid entering wait methods under */
21 /* cooperative suspend of a thread. Under preemptive suspend a thread gets */
22 /* a queued APC as part of an alertable suspend request. The APC will break any wait's */
23 /* done by any of below methods. In hybrid suspend, if a thread gets into a GC safe area */
24 /* thread will be preemptive suspend as above and an APC will be queued, breaking any wait. */
25 /* If the thread is not within a GC safe area, a cooperative suspend will be used, but that */
26 /* won't queue an APC to the thread, so in cases where we enter a GC safe area and a wait */
27 /* using below functions, that wait won't be alerted. This could be solved using */
28 /* interrupt handlers. Problem with interrupt handlers on Windows together with APC is race */
29 /* between thread executing interrupt handler and current thread. We will need the thread */
30 /* alive when posting the APC, but since there is no synchronization between waiting thread */
31 /* and thread running interrupt handler, waiting thread could already be terminated when executing */
32 /* interrupt handler. There are ways to mitigate this, but using below schema is more lightweight and */
33 /* solves the same problem + gives some additional benefits on preemptive suspend. Wait methods */
34 /* will register a empty interrupt handler. This is needed in order to correctly get current alertable */
35 /* state of the thread when register/unregister handler. If thread is already interrupted, we can */
36 /* ignore call to wait method and return alertable error code. This solves the cooperative suspend */
37 /* scenario since we evaluate the current interrupt state inside GC safe block. If not yet interrupted, */
38 /* cooperative suspend will detect that thread is inside a GC safe block so it will interrupt kernel */
39 /* as part of suspend request (similar to preemptive suspend) queuing APC, breaking any waits. */
40 static void
41 win32_wait_interrupt_handler (gpointer ignored)
45 /* Evaluate if we have a pending interrupt on current thread before */
46 /* entering wait. If thread has been cooperative suspended, it won't */
47 /* always queue an APC (only when already in a GC safe block), but since */
48 /* we should be inside a GC safe block at this point, checking current */
49 /* thread's interrupt state will tell us if we have a pending interrupt. */
50 /* If not, we will get an APC queued to break any waits if interrupted */
51 /* after this check (both in cooperative and preemptive suspend modes). */
52 #define WIN32_CHECK_INTERRUPT(info, alertable) \
53 do { \
54 MONO_REQ_GC_SAFE_MODE; \
55 if (alertable && info && mono_thread_info_is_interrupt_state (info)) { \
56 SetLastError (WAIT_IO_COMPLETION); \
57 return WAIT_IO_COMPLETION; \
58 } \
59 } while (0)
61 #define WIN32_ENTER_ALERTABLE_WAIT(info) \
62 do { \
63 if (info) { \
64 gboolean alerted = FALSE; \
65 mono_thread_info_install_interrupt (win32_wait_interrupt_handler, NULL, &alerted); \
66 if (alerted) { \
67 SetLastError (WAIT_IO_COMPLETION); \
68 return WAIT_IO_COMPLETION; \
69 } \
70 mono_win32_enter_alertable_wait (info); \
71 } \
72 } while (0)
74 #define WIN32_LEAVE_ALERTABLE_WAIT(info) \
75 do { \
76 if (info) { \
77 gboolean alerted = FALSE; \
78 mono_win32_leave_alertable_wait (info); \
79 mono_thread_info_uninstall_interrupt (&alerted); \
80 } \
81 } while (0)
83 static DWORD
84 win32_sleep_ex_interrupt_checked (MonoThreadInfo *info, DWORD timeout, BOOL alertable)
86 WIN32_CHECK_INTERRUPT (info, alertable);
87 return SleepEx (timeout, alertable);
90 static DWORD
91 win32_sleep_ex (DWORD timeout, BOOL alertable, BOOL cooperative)
93 DWORD result = WAIT_FAILED;
94 MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL;
96 WIN32_ENTER_ALERTABLE_WAIT (info);
98 if (cooperative) {
99 MONO_ENTER_GC_SAFE;
100 result = win32_sleep_ex_interrupt_checked (info, timeout, alertable);
101 MONO_EXIT_GC_SAFE;
102 } else {
103 result = win32_sleep_ex_interrupt_checked (info, timeout, alertable);
106 WIN32_LEAVE_ALERTABLE_WAIT (info);
108 return result;
111 DWORD
112 mono_win32_sleep_ex (DWORD timeout, BOOL alertable)
114 return win32_sleep_ex (timeout, alertable, FALSE);
117 DWORD
118 mono_coop_win32_sleep_ex (DWORD timeout, BOOL alertable)
120 return win32_sleep_ex (timeout, alertable, TRUE);
123 static DWORD
124 win32_wait_for_single_object_ex_interrupt_checked (MonoThreadInfo *info, HANDLE handle, DWORD timeout, BOOL alertable)
126 WIN32_CHECK_INTERRUPT (info, alertable);
127 return WaitForSingleObjectEx (handle, timeout, alertable);
130 static DWORD
131 win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertable, BOOL cooperative)
133 DWORD result = WAIT_FAILED;
134 MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL;
136 WIN32_ENTER_ALERTABLE_WAIT (info);
138 if (cooperative) {
139 MONO_ENTER_GC_SAFE;
140 result = win32_wait_for_single_object_ex_interrupt_checked (info, handle, timeout, alertable);
141 MONO_EXIT_GC_SAFE;
142 } else {
143 result = win32_wait_for_single_object_ex_interrupt_checked (info, handle, timeout, alertable);
146 WIN32_LEAVE_ALERTABLE_WAIT (info);
148 return result;
151 DWORD
152 mono_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertable)
154 return win32_wait_for_single_object_ex (handle, timeout, alertable, FALSE);
157 DWORD
158 mono_coop_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertable)
160 return win32_wait_for_single_object_ex (handle, timeout, alertable, TRUE);
163 static DWORD
164 win32_wait_for_multiple_objects_ex_interrupt_checked (MonoThreadInfo *info, DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
166 WIN32_CHECK_INTERRUPT (info, alertable);
167 return WaitForMultipleObjectsEx (count, handles, waitAll, timeout, alertable);
170 static DWORD
171 win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable, MonoError *error, BOOL cooperative)
173 DWORD result = WAIT_FAILED;
174 MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL;
176 WIN32_ENTER_ALERTABLE_WAIT (info);
178 if (cooperative) {
179 MONO_ENTER_GC_SAFE;
180 result = win32_wait_for_multiple_objects_ex_interrupt_checked (info, count, handles, waitAll, timeout, alertable);
181 MONO_EXIT_GC_SAFE;
182 } else {
183 result = win32_wait_for_multiple_objects_ex_interrupt_checked (info, count, handles, waitAll, timeout, alertable);
186 WIN32_LEAVE_ALERTABLE_WAIT (info);
188 // This is not perfect, but it is the best you can do in usermode and matches CoreCLR.
189 // i.e. handle-based instead of object-based.
191 if (result == WAIT_FAILED && waitAll && error &&
192 count > 1 && count <= MAXIMUM_WAIT_OBJECTS
193 && GetLastError () == ERROR_INVALID_PARAMETER) {
194 gpointer handles_sorted [MAXIMUM_WAIT_OBJECTS]; // 64
195 memcpy (handles_sorted, handles, count * sizeof (handles [0]));
196 mono_qsort (handles_sorted, count, sizeof (handles_sorted [0]), g_direct_equal);
197 for (DWORD i = 1; i < count; ++i) {
198 if (handles_sorted [i - 1] == handles_sorted [i]) {
199 mono_error_set_duplicate_wait_object (error);
200 mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p is duplicated", __func__, handles_sorted [i]);
201 // Preserve LastError, but reduce triggering write breakpoints.
202 if (GetLastError () != ERROR_INVALID_PARAMETER)
203 SetLastError (ERROR_INVALID_PARAMETER);
204 break;
209 return result;
212 DWORD
213 mono_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable, MonoError *error)
215 return win32_wait_for_multiple_objects_ex (count, handles, waitAll, timeout, alertable, error, FALSE);
218 DWORD
219 mono_coop_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable, MonoError *error)
221 return win32_wait_for_multiple_objects_ex (count, handles, waitAll, timeout, alertable, error, TRUE);
224 #if HAVE_API_SUPPORT_WIN32_SIGNAL_OBJECT_AND_WAIT
226 static DWORD
227 win32_signal_object_and_wait_interrupt_checked (MonoThreadInfo *info, HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable)
229 WIN32_CHECK_INTERRUPT (info, alertable);
230 return SignalObjectAndWait (toSignal, toWait, timeout, alertable);
233 static DWORD
234 win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable, BOOL cooperative)
236 DWORD result = WAIT_FAILED;
237 MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL;
239 WIN32_ENTER_ALERTABLE_WAIT (info);
241 if (cooperative) {
242 MONO_ENTER_GC_SAFE;
243 result = win32_signal_object_and_wait_interrupt_checked (info, toSignal, toWait, timeout, alertable);
244 MONO_EXIT_GC_SAFE;
245 } else {
246 result = win32_signal_object_and_wait_interrupt_checked (info, toSignal, toWait, timeout, alertable);
249 WIN32_LEAVE_ALERTABLE_WAIT (info);
251 return result;
254 DWORD
255 mono_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable)
257 return win32_signal_object_and_wait (toSignal, toWait, timeout, alertable, FALSE);
260 DWORD
261 mono_coop_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable)
263 return win32_signal_object_and_wait (toSignal, toWait, timeout, alertable, TRUE);
266 #endif /* HAVE_API_SUPPORT_WIN32_SIGNAL_OBJECT_AND_WAIT */
268 #if HAVE_API_SUPPORT_WIN32_MSG_WAIT_FOR_MULTIPLE_OBJECTS
269 static DWORD
270 win32_msg_wait_for_multiple_objects_ex_interrupt_checked (MonoThreadInfo *info, DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags, BOOL alertable)
272 WIN32_CHECK_INTERRUPT (info, alertable);
273 return MsgWaitForMultipleObjectsEx (count, handles, timeout, wakeMask, flags);
276 static DWORD
277 win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags, BOOL cooperative)
279 DWORD result = WAIT_FAILED;
280 BOOL alertable = flags & MWMO_ALERTABLE;
281 MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL;
283 WIN32_ENTER_ALERTABLE_WAIT (info);
285 if (cooperative) {
286 MONO_ENTER_GC_SAFE;
287 result = win32_msg_wait_for_multiple_objects_ex_interrupt_checked (info, count, handles, timeout, wakeMask, flags, alertable);
288 MONO_EXIT_GC_SAFE;
289 } else {
290 result = win32_msg_wait_for_multiple_objects_ex_interrupt_checked (info, count, handles, timeout, wakeMask, flags, alertable);
293 WIN32_LEAVE_ALERTABLE_WAIT (info);
295 return result;
298 DWORD
299 mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags)
301 return win32_msg_wait_for_multiple_objects_ex (count, handles, timeout, wakeMask, flags, FALSE);
304 DWORD
305 mono_coop_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags)
307 return win32_msg_wait_for_multiple_objects_ex (count, handles, timeout, wakeMask, flags, TRUE);
309 #endif /* HAVE_API_SUPPORT_WIN32_MSG_WAIT_FOR_MULTIPLE_OBJECTS */
311 static DWORD
312 win32_wsa_wait_for_multiple_events_interrupt_checked (MonoThreadInfo *info, DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
314 WIN32_CHECK_INTERRUPT (info, alertable);
315 return WSAWaitForMultipleEvents (count, handles, waitAll, timeout, alertable);
318 static DWORD
319 win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable, BOOL cooperative)
321 DWORD result = WAIT_FAILED;
322 MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL;
324 WIN32_ENTER_ALERTABLE_WAIT (info);
326 if (cooperative) {
327 MONO_ENTER_GC_SAFE;
328 result = win32_wsa_wait_for_multiple_events_interrupt_checked (info, count, handles, waitAll, timeout, alertable);
329 MONO_EXIT_GC_SAFE;
330 } else {
331 result = win32_wsa_wait_for_multiple_events_interrupt_checked (info, count, handles, waitAll, timeout, alertable);
334 WIN32_LEAVE_ALERTABLE_WAIT (info);
336 return result;
339 DWORD
340 mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
342 return win32_wsa_wait_for_multiple_events (count, handles, waitAll, timeout, alertable, FALSE);
345 DWORD
346 mono_coop_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
348 return win32_wsa_wait_for_multiple_events (count, handles, waitAll, timeout, alertable, TRUE);