[System]: Fix WebConnectionStream.SetHeadersAsync() logic for #31830.
[mono.git] / mono / utils / mono-threads-windows.c
1 /*
2  * mono-threads-windows.c: Low-level threading, windows version
3  *
4  * Author:
5  *      Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * (C) 2011 Novell, Inc
8  */
9
10 #include <mono/utils/mono-threads.h>
11
12 #if defined(USE_WINDOWS_BACKEND)
13
14 #include <mono/utils/mono-compiler.h>
15 #include <limits.h>
16
17
18 void
19 mono_threads_init_platform (void)
20 {
21 }
22
23 static void CALLBACK
24 interrupt_apc (ULONG_PTR param)
25 {
26 }
27
28 void
29 mono_threads_core_abort_syscall (MonoThreadInfo *info)
30 {
31         DWORD id = mono_thread_info_get_tid (info);
32         HANDLE handle;
33
34         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
35         g_assert (handle);
36
37         QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
38
39         CloseHandle (handle);
40 }
41
42 gboolean
43 mono_threads_core_needs_abort_syscall (void)
44 {
45         return TRUE;
46 }
47
48 gboolean
49 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
50 {
51         DWORD id = mono_thread_info_get_tid (info);
52         HANDLE handle;
53         DWORD result;
54         gboolean res;
55
56         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
57         g_assert (handle);
58
59         result = SuspendThread (handle);
60         THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
61         if (result == (DWORD)-1) {
62                 CloseHandle (handle);
63                 return FALSE;
64         }
65
66         /* We're in the middle of a self-suspend, resume and register */
67         if (!mono_threads_transition_finish_async_suspend (info)) {
68                 mono_threads_add_to_pending_operation_set (info);
69                 result = ResumeThread (handle);
70                 g_assert (result == 1);
71                 CloseHandle (handle);
72                 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
73                 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
74                 return TRUE;
75         }
76         res = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
77         THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
78         if (res) {
79                 //FIXME do we need to QueueUserAPC on this case?
80                 if (interrupt_kernel)
81                         QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
82         } else {
83                 mono_threads_transition_async_suspend_compensation (info);
84                 result = ResumeThread (handle);
85                 g_assert (result == 1);
86                 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
87         }
88
89         CloseHandle (handle);
90         return res;
91 }
92
93 gboolean
94 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
95 {
96         return TRUE;
97 }
98
99 gboolean
100 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
101 {
102         DWORD id = mono_thread_info_get_tid (info);
103         HANDLE handle;
104         DWORD result;
105
106         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
107         g_assert (handle);
108
109         if (info->async_target) {
110                 MonoContext ctx;
111                 CONTEXT context;
112                 gboolean res;
113
114                 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
115                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
116                 info->async_target = info->user_data = NULL;
117
118                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
119
120                 if (!GetThreadContext (handle, &context)) {
121                         CloseHandle (handle);
122                         return FALSE;
123                 }
124
125                 g_assert (context.ContextFlags & CONTEXT_INTEGER);
126                 g_assert (context.ContextFlags & CONTEXT_CONTROL);
127
128                 mono_monoctx_to_sigctx (&ctx, &context);
129
130                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
131                 res = SetThreadContext (handle, &context);
132                 if (!res) {
133                         CloseHandle (handle);
134                         return FALSE;
135                 }
136         }
137
138         result = ResumeThread (handle);
139         CloseHandle (handle);
140
141         return result != (DWORD)-1;
142 }
143
144
145 void
146 mono_threads_platform_register (MonoThreadInfo *info)
147 {
148 }
149
150 void
151 mono_threads_platform_free (MonoThreadInfo *info)
152 {
153 }
154
155 void
156 mono_threads_core_begin_global_suspend (void)
157 {
158 }
159
160 void
161 mono_threads_core_end_global_suspend (void)
162 {
163 }
164
165 #endif
166
167 #if defined (HOST_WIN32)
168
169 typedef struct {
170         LPTHREAD_START_ROUTINE start_routine;
171         void *arg;
172         MonoSemType registered;
173         gboolean suspend;
174         HANDLE suspend_event;
175 } ThreadStartInfo;
176
177 static DWORD WINAPI
178 inner_start_thread (LPVOID arg)
179 {
180         ThreadStartInfo *start_info = arg;
181         void *t_arg = start_info->arg;
182         int post_result;
183         LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
184         DWORD result;
185         gboolean suspend = start_info->suspend;
186         HANDLE suspend_event = start_info->suspend_event;
187         MonoThreadInfo *info;
188
189         info = mono_thread_info_attach (&result);
190         info->runtime_thread = TRUE;
191         info->create_suspended = suspend;
192
193         post_result = MONO_SEM_POST (&(start_info->registered));
194         g_assert (!post_result);
195
196         if (suspend) {
197                 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
198                 CloseHandle (suspend_event);
199         }
200
201         result = start_func (t_arg);
202
203         mono_thread_info_detach ();
204
205         return result;
206 }
207
208 HANDLE
209 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
210 {
211         ThreadStartInfo *start_info;
212         HANDLE result;
213         DWORD thread_id;
214
215         start_info = g_malloc0 (sizeof (ThreadStartInfo));
216         if (!start_info)
217                 return NULL;
218         MONO_SEM_INIT (&(start_info->registered), 0);
219         start_info->arg = arg;
220         start_info->start_routine = start_routine;
221         start_info->suspend = creation_flags & CREATE_SUSPENDED;
222         creation_flags &= ~CREATE_SUSPENDED;
223         if (start_info->suspend) {
224                 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
225                 if (!start_info->suspend_event)
226                         return NULL;
227         }
228
229         result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
230         if (result) {
231                 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
232                         /*if (EINTR != errno) ABORT("sem_wait failed"); */
233                 }
234                 if (start_info->suspend) {
235                         g_assert (SuspendThread (result) != (DWORD)-1);
236                         SetEvent (start_info->suspend_event);
237                 }
238         } else if (start_info->suspend) {
239                 CloseHandle (start_info->suspend_event);
240         }
241         if (out_tid)
242                 *out_tid = thread_id;
243         MONO_SEM_DESTROY (&(start_info->registered));
244         g_free (start_info);
245         return result;
246 }
247
248
249 MonoNativeThreadId
250 mono_native_thread_id_get (void)
251 {
252         return GetCurrentThreadId ();
253 }
254
255 gboolean
256 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
257 {
258         return id1 == id2;
259 }
260
261 gboolean
262 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
263 {
264         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
265 }
266
267 void
268 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
269 {
270         HANDLE handle;
271
272         handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
273         g_assert (handle);
274         ResumeThread (handle);
275         CloseHandle (handle);
276 }
277
278 #if HAVE_DECL___READFSDWORD==0
279 static MONO_ALWAYS_INLINE unsigned long long
280 __readfsdword (unsigned long offset)
281 {
282         unsigned long value;
283         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
284    __asm__ volatile ("movl    %%fs:%1,%0"
285      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
286         return value;
287 }
288 #endif
289
290 void
291 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
292 {
293         MEMORY_BASIC_INFORMATION meminfo;
294 #ifdef _WIN64
295         /* win7 apis */
296         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
297         guint8 *stackTop = (guint8*)tib->StackBase;
298         guint8 *stackBottom = (guint8*)tib->StackLimit;
299 #else
300         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
301         void* tib = (void*)__readfsdword(0x18);
302         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
303         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
304 #endif
305         /*
306         Windows stacks are expanded on demand, one page at time. The TIB reports
307         only the currently allocated amount.
308         VirtualQuery will return the actual limit for the bottom, which is what we want.
309         */
310         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
311                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
312
313         *staddr = stackBottom;
314         *stsize = stackTop - stackBottom;
315
316 }
317
318 gboolean
319 mono_threads_core_yield (void)
320 {
321         return SwitchToThread ();
322 }
323
324 void
325 mono_threads_core_exit (int exit_code)
326 {
327         ExitThread (exit_code);
328 }
329
330 void
331 mono_threads_core_unregister (MonoThreadInfo *info)
332 {
333 }
334
335 HANDLE
336 mono_threads_core_open_handle (void)
337 {
338         HANDLE thread_handle;
339
340         thread_handle = GetCurrentThread ();
341         g_assert (thread_handle);
342
343         /*
344          * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
345          * refer to the thread from other threads for things like aborting.
346          */
347         DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
348                                          THREAD_ALL_ACCESS, TRUE, 0);
349
350         return thread_handle;
351 }
352
353 int
354 mono_threads_get_max_stack_size (void)
355 {
356         //FIXME
357         return INT_MAX;
358 }
359
360 HANDLE
361 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
362 {
363         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
364 }
365
366 #if defined(_MSC_VER)
367 const DWORD MS_VC_EXCEPTION=0x406D1388;
368 #pragma pack(push,8)
369 typedef struct tagTHREADNAME_INFO
370 {
371    DWORD dwType; // Must be 0x1000.
372    LPCSTR szName; // Pointer to name (in user addr space).
373    DWORD dwThreadID; // Thread ID (-1=caller thread).
374   DWORD dwFlags; // Reserved for future use, must be zero.
375 } THREADNAME_INFO;
376 #pragma pack(pop)
377 #endif
378
379 void
380 mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
381 {
382 #if defined(_MSC_VER)
383         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
384         THREADNAME_INFO info;
385         info.dwType = 0x1000;
386         info.szName = name;
387         info.dwThreadID = tid;
388         info.dwFlags = 0;
389
390         __try {
391                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
392         }
393         __except(EXCEPTION_EXECUTE_HANDLER) {
394         }
395 #endif
396 }
397
398
399 gpointer
400 mono_threads_core_prepare_interrupt (HANDLE thread_handle)
401 {
402         return NULL;
403 }
404
405 void
406 mono_threads_core_finish_interrupt (gpointer wait_handle)
407 {
408 }
409
410 void
411 mono_threads_core_self_interrupt (void)
412 {
413 }
414
415 void
416 mono_threads_core_clear_interruption (void)
417 {
418 }
419
420 #endif