Merge pull request #3511 from henricm/fix-no-socket-reuse
[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_suspend_init (void)
20 {
21 }
22
23 static void CALLBACK
24 interrupt_apc (ULONG_PTR param)
25 {
26 }
27
28 gboolean
29 mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
30 {
31         DWORD id = mono_thread_info_get_tid (info);
32         HANDLE handle;
33         DWORD result;
34
35         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
36         g_assert (handle);
37
38         result = SuspendThread (handle);
39         THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
40         if (result == (DWORD)-1) {
41                 CloseHandle (handle);
42                 return FALSE;
43         }
44
45         /* We're in the middle of a self-suspend, resume and register */
46         if (!mono_threads_transition_finish_async_suspend (info)) {
47                 mono_threads_add_to_pending_operation_set (info);
48                 result = ResumeThread (handle);
49                 g_assert (result == 1);
50                 CloseHandle (handle);
51                 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
52                 //XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
53                 return TRUE;
54         }
55         info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
56         THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
57         if (info->suspend_can_continue) {
58                 //FIXME do we need to QueueUserAPC on this case?
59                 if (interrupt_kernel)
60                         QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
61         } else {
62                 THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
63         }
64
65         CloseHandle (handle);
66         return info->suspend_can_continue;
67 }
68
69 gboolean
70 mono_threads_suspend_check_suspend_result (MonoThreadInfo *info)
71 {
72         return info->suspend_can_continue;
73 }
74
75 gboolean
76 mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
77 {
78         DWORD id = mono_thread_info_get_tid (info);
79         HANDLE handle;
80         DWORD result;
81
82         handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
83         g_assert (handle);
84
85         if (info->async_target) {
86                 MonoContext ctx;
87                 CONTEXT context;
88                 gboolean res;
89
90                 ctx = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
91                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&ctx, info->async_target, info->user_data);
92                 info->async_target = info->user_data = NULL;
93
94                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
95
96                 if (!GetThreadContext (handle, &context)) {
97                         CloseHandle (handle);
98                         return FALSE;
99                 }
100
101                 g_assert (context.ContextFlags & CONTEXT_INTEGER);
102                 g_assert (context.ContextFlags & CONTEXT_CONTROL);
103
104                 mono_monoctx_to_sigctx (&ctx, &context);
105
106                 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
107                 res = SetThreadContext (handle, &context);
108                 if (!res) {
109                         CloseHandle (handle);
110                         return FALSE;
111                 }
112         }
113
114         result = ResumeThread (handle);
115         CloseHandle (handle);
116
117         return result != (DWORD)-1;
118 }
119
120
121 void
122 mono_threads_suspend_register (MonoThreadInfo *info)
123 {
124 }
125
126 void
127 mono_threads_suspend_free (MonoThreadInfo *info)
128 {
129 }
130
131 #endif
132
133 #if defined (HOST_WIN32)
134
135 void
136 mono_threads_platform_register (MonoThreadInfo *info)
137 {
138         HANDLE thread_handle;
139
140         thread_handle = GetCurrentThread ();
141         g_assert (thread_handle);
142
143         /* The handle returned by GetCurrentThread () is a pseudo handle, so it can't
144          * be used to refer to the thread from other threads for things like aborting. */
145         DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
146
147         g_assert (!info->handle);
148         info->handle = thread_handle;
149 }
150
151 int
152 mono_threads_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize stack_size, MonoNativeThreadId *out_tid)
153 {
154         HANDLE result;
155         DWORD thread_id;
156
157         result = CreateThread (NULL, stack_size, (LPTHREAD_START_ROUTINE) thread_fn, thread_data, 0, &thread_id);
158         if (!result)
159                 return -1;
160
161         /* A new handle is open when attaching
162          * the thread, so we don't need this one */
163         CloseHandle (result);
164
165         if (out_tid)
166                 *out_tid = thread_id;
167
168         return 0;
169 }
170
171
172 MonoNativeThreadId
173 mono_native_thread_id_get (void)
174 {
175         return GetCurrentThreadId ();
176 }
177
178 gboolean
179 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
180 {
181         return id1 == id2;
182 }
183
184 gboolean
185 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
186 {
187         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
188 }
189
190 #if HAVE_DECL___READFSDWORD==0
191 static MONO_ALWAYS_INLINE unsigned long long
192 __readfsdword (unsigned long offset)
193 {
194         unsigned long value;
195         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
196    __asm__ volatile ("movl    %%fs:%1,%0"
197      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
198         return value;
199 }
200 #endif
201
202 void
203 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
204 {
205         MEMORY_BASIC_INFORMATION meminfo;
206 #ifdef _WIN64
207         /* win7 apis */
208         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
209         guint8 *stackTop = (guint8*)tib->StackBase;
210         guint8 *stackBottom = (guint8*)tib->StackLimit;
211 #else
212         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
213         void* tib = (void*)__readfsdword(0x18);
214         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
215         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
216 #endif
217         /*
218         Windows stacks are expanded on demand, one page at time. The TIB reports
219         only the currently allocated amount.
220         VirtualQuery will return the actual limit for the bottom, which is what we want.
221         */
222         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
223                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
224
225         *staddr = stackBottom;
226         *stsize = stackTop - stackBottom;
227
228 }
229
230 gboolean
231 mono_threads_platform_yield (void)
232 {
233         return SwitchToThread ();
234 }
235
236 void
237 mono_threads_platform_exit (int exit_code)
238 {
239         mono_thread_info_detach ();
240         ExitThread (exit_code);
241 }
242
243 void
244 mono_threads_platform_unregister (MonoThreadInfo *info)
245 {
246         mono_threads_platform_set_exited (info);
247 }
248
249 int
250 mono_threads_get_max_stack_size (void)
251 {
252         //FIXME
253         return INT_MAX;
254 }
255
256 HANDLE
257 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
258 {
259         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
260 }
261
262 void
263 mono_threads_platform_close_thread_handle (HANDLE handle)
264 {
265         CloseHandle (handle);
266 }
267
268 #if defined(_MSC_VER)
269 const DWORD MS_VC_EXCEPTION=0x406D1388;
270 #pragma pack(push,8)
271 typedef struct tagTHREADNAME_INFO
272 {
273    DWORD dwType; // Must be 0x1000.
274    LPCSTR szName; // Pointer to name (in user addr space).
275    DWORD dwThreadID; // Thread ID (-1=caller thread).
276   DWORD dwFlags; // Reserved for future use, must be zero.
277 } THREADNAME_INFO;
278 #pragma pack(pop)
279 #endif
280
281 void
282 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
283 {
284 #if defined(_MSC_VER)
285         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
286         THREADNAME_INFO info;
287         info.dwType = 0x1000;
288         info.szName = name;
289         info.dwThreadID = tid;
290         info.dwFlags = 0;
291
292         __try {
293                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
294         }
295         __except(EXCEPTION_EXECUTE_HANDLER) {
296         }
297 #endif
298 }
299
300 void
301 mono_threads_platform_set_exited (MonoThreadInfo *info)
302 {
303         g_assert (info->handle);
304         // No need to call CloseHandle() here since the InternalThread
305         // destructor will close the handle when the finalizer thread calls it
306         info->handle = NULL;
307 }
308
309 void
310 mono_threads_platform_describe (MonoThreadInfo *info, GString *text)
311 {
312         /* TODO */
313 }
314
315 void
316 mono_threads_platform_own_mutex (MonoThreadInfo *info, gpointer mutex_handle)
317 {
318         g_assert_not_reached ();
319 }
320
321 void
322 mono_threads_platform_disown_mutex (MonoThreadInfo *info, gpointer mutex_handle)
323 {
324         g_assert_not_reached ();
325 }
326
327 MonoThreadPriority
328 mono_threads_platform_get_priority (MonoThreadInfo *info)
329 {
330         g_assert (info->handle);
331         return GetThreadPriority (info->handle) + 2;
332 }
333
334 void
335 mono_threads_platform_set_priority (MonoThreadInfo *info, MonoThreadPriority priority)
336 {
337         BOOL res;
338
339         g_assert (info->handle);
340
341         res = SetThreadPriority (info->handle, priority - 2);
342         if (!res)
343                 g_error ("%s: SetThreadPriority failed, error %d", __func__, GetLastError ());
344 }
345
346 void
347 mono_threads_platform_init (void)
348 {
349 }
350
351 #endif