Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / utils / mono-os-wait-win32.c
1 /**
2 * \file
3 * Win32 OS wait wrappers and interrupt/abort APC handling.
4 *
5 * Author:
6 *   Johan Lorensson (lateralusx.github@gmail.com)
7 *
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9 */
10
11 #include <mono/utils/mono-os-wait.h>
12 #include <mono/utils/mono-threads.h>
13 #include <mono/utils/mono-threads-debug.h>
14
15 enum ThreadWaitInfo {
16         THREAD_WAIT_INFO_CLEARED = 0,
17         THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT = 1 << 0,
18         THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT = 1 << 1,
19         THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT = 1 << 2
20 };
21
22 static inline void
23 request_interrupt (gpointer thread_info, HANDLE native_thread_handle, gint32 pending_apc_slot, PAPCFUNC apc_callback, DWORD tid)
24 {
25         /*
26         * On Windows platforms, an async interrupt/abort request queues an APC
27         * that needs to be processed by target thread before it can return from an
28         * alertable OS wait call and complete the mono interrupt/abort request.
29         * Uncontrolled queuing of APC's could flood the APC queue preventing the target thread
30         * to return from its alertable OS wait call, blocking the interrupt/abort requests to complete
31         * This check makes sure that only one APC per type gets queued, preventing potential flooding
32         * of the APC queue. NOTE, this code will execute regardless if targeted thread is currently in
33         * an alertable wait or not. This is done to prevent races between interrupt/abort requests and
34         * alertable wait calls. Threads already in an alertable wait should handle WAIT_IO_COMPLETION
35         * return scenarios and restart the alertable wait operation if needed or take other actions
36         * (like service the interrupt/abort request).
37         */
38         MonoThreadInfo *info = (MonoThreadInfo *)thread_info;
39         gint32 old_wait_info, new_wait_info;
40
41         do {
42                 old_wait_info = InterlockedRead (&info->thread_wait_info);
43                 if (old_wait_info & pending_apc_slot)
44                         return;
45
46                 new_wait_info = old_wait_info | pending_apc_slot;
47         } while (InterlockedCompareExchange (&info->thread_wait_info, new_wait_info, old_wait_info) != old_wait_info);
48
49         THREADS_INTERRUPT_DEBUG ("%06d - Interrupting/Aborting syscall in thread %06d", GetCurrentThreadId (), tid);
50         QueueUserAPC (apc_callback, native_thread_handle, (ULONG_PTR)NULL);
51 }
52
53 static void CALLBACK
54 interrupt_apc (ULONG_PTR param)
55 {
56         THREADS_INTERRUPT_DEBUG ("%06d - interrupt_apc () called", GetCurrentThreadId ());
57 }
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, THREAD_WAIT_INFO_PENDING_INTERRUPT_APC_SLOT, interrupt_apc, tid);
63 }
64
65 static void CALLBACK
66 abort_apc (ULONG_PTR param)
67 {
68         THREADS_INTERRUPT_DEBUG ("%06d - abort_apc () called", GetCurrentThreadId ());
69 }
70
71 void
72 mono_win32_abort_wait (PVOID thread_info, HANDLE native_thread_handle, DWORD tid)
73 {
74         request_interrupt (thread_info, native_thread_handle, THREAD_WAIT_INFO_PENDING_ABORT_APC_SLOT, abort_apc, tid);
75 }
76
77 static inline void
78 enter_alertable_wait (MonoThreadInfo *info)
79 {
80         // Clear any previous flags. Set alertable wait flag.
81         InterlockedExchange (&info->thread_wait_info, THREAD_WAIT_INFO_ALERTABLE_WAIT_SLOT);
82 }
83
84 static inline void
85 leave_alertable_wait (MonoThreadInfo *info)
86 {
87         // Clear any previous flags. Thread is exiting alertable wait state, and info around pending interrupt/abort APC's
88         // can now be discarded as well, thread is out of wait operation and can proceed it's execution.
89         InterlockedExchange (&info->thread_wait_info, THREAD_WAIT_INFO_CLEARED);
90 }
91
92 DWORD
93 mono_win32_sleep_ex (DWORD timeout, BOOL alertable)
94 {
95         DWORD result = WAIT_FAILED;
96         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
97
98         if (alertable && info) {
99                 enter_alertable_wait (info);
100         }
101
102         result = SleepEx (timeout, alertable);
103
104         // NOTE, leave_alertable_wait should not affect GetLastError but
105         // if changed, GetLastError needs to be preserved and reset before returning.
106         if (alertable && info) {
107                 leave_alertable_wait (info);
108         }
109
110         return result;
111 }
112
113 DWORD
114 mono_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertable)
115 {
116         DWORD result = WAIT_FAILED;
117         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
118
119         if (alertable && info) {
120                 enter_alertable_wait (info);
121         }
122
123         result = WaitForSingleObjectEx (handle, timeout, alertable);
124
125         // NOTE, leave_alertable_wait should not affect GetLastError but
126         // if changed, GetLastError needs to be preserved and reset before returning.
127         if (alertable && info) {
128                 leave_alertable_wait (info);
129         }
130
131         return result;
132 }
133
134 DWORD
135 mono_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
136 {
137         DWORD result = WAIT_FAILED;
138         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
139
140         if (alertable && info) {
141                 enter_alertable_wait (info);
142         }
143
144         result = WaitForMultipleObjectsEx (count, handles, waitAll, timeout, alertable);
145
146         // NOTE, leave_alertable_wait should not affect GetLastError but
147         // if changed, GetLastError needs to be preserved and reset before returning.
148         if (alertable && info) {
149                 leave_alertable_wait (info);
150         }
151
152         return result;
153 }
154
155 DWORD
156 mono_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable)
157 {
158         DWORD result = WAIT_FAILED;
159         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
160
161         if (alertable && info) {
162                 enter_alertable_wait (info);
163         }
164
165         result = SignalObjectAndWait (toSignal, toWait, timeout, alertable);
166
167         // NOTE, leave_alertable_wait should not affect GetLastError but
168         // if changed, GetLastError needs to be preserved and reset before returning.
169         if (alertable && info) {
170                 leave_alertable_wait (info);
171         }
172
173         return result;
174 }
175
176 DWORD
177 mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags)
178 {
179         DWORD result = WAIT_FAILED;
180         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
181         BOOL alertable = flags & MWMO_ALERTABLE;
182
183         if (alertable && info) {
184                 enter_alertable_wait (info);
185         }
186
187         result = MsgWaitForMultipleObjectsEx (count, handles, timeout, wakeMask, flags);
188
189         // NOTE, leave_alertable_wait should not affect GetLastError but
190         // if changed, GetLastError needs to be preserved and reset before returning.
191         if (alertable && info) {
192                 leave_alertable_wait (info);
193         }
194
195         return result;
196 }
197
198 DWORD
199 mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable)
200 {
201         DWORD result = WAIT_FAILED;
202         MonoThreadInfo *info = mono_thread_info_current_unchecked ();
203
204         if (alertable && info) {
205                 enter_alertable_wait (info);
206         }
207
208         result = WSAWaitForMultipleEvents (count, handles, waitAll, timeout, alertable);
209
210         // NOTE, leave_alertable_wait should not affect GetLastError but
211         // if changed, GetLastError needs to be preserved and reset before returning.
212         if (alertable && info) {
213                 leave_alertable_wait (info);
214         }
215
216         return result;
217 }