Merge pull request #3199 from lambdageek/dev/proxy-setter
[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 gboolean
29 mono_threads_core_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_core_check_suspend_result (MonoThreadInfo *info)
71 {
72         return info->suspend_can_continue;
73 }
74
75 gboolean
76 mono_threads_core_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_platform_register (MonoThreadInfo *info)
123 {
124 }
125
126 void
127 mono_threads_platform_free (MonoThreadInfo *info)
128 {
129 }
130
131 #endif
132
133 #if defined (HOST_WIN32)
134
135 typedef struct {
136         LPTHREAD_START_ROUTINE start_routine;
137         void *arg;
138         MonoCoopSem registered;
139         gboolean suspend;
140         HANDLE suspend_event;
141 } ThreadStartInfo;
142
143 static DWORD WINAPI
144 inner_start_thread (LPVOID arg)
145 {
146         ThreadStartInfo *start_info = arg;
147         void *t_arg = start_info->arg;
148         int post_result;
149         LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
150         DWORD result;
151         gboolean suspend = start_info->suspend;
152         HANDLE suspend_event = start_info->suspend_event;
153         MonoThreadInfo *info;
154
155         info = mono_thread_info_attach (&result);
156         info->runtime_thread = TRUE;
157         info->create_suspended = suspend;
158
159         mono_coop_sem_post (&(start_info->registered));
160
161         if (suspend) {
162                 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
163                 CloseHandle (suspend_event);
164         }
165
166         result = start_func (t_arg);
167
168         mono_thread_info_detach ();
169
170         return result;
171 }
172
173 HANDLE
174 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, MonoThreadParm *tp, MonoNativeThreadId *out_tid)
175 {
176         ThreadStartInfo *start_info;
177         HANDLE result;
178         DWORD thread_id;
179         guint32 creation_flags = tp->creation_flags;
180         int res;
181
182         start_info = g_malloc0 (sizeof (ThreadStartInfo));
183         if (!start_info)
184                 return NULL;
185         mono_coop_sem_init (&(start_info->registered), 0);
186         start_info->arg = arg;
187         start_info->start_routine = start_routine;
188         start_info->suspend = creation_flags & CREATE_SUSPENDED;
189         creation_flags &= ~CREATE_SUSPENDED;
190         if (start_info->suspend) {
191                 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
192                 if (!start_info->suspend_event)
193                         return NULL;
194         }
195
196         result = CreateThread (NULL, tp->stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
197         if (result) {
198                 res = mono_coop_sem_wait (&(start_info->registered), MONO_SEM_FLAGS_NONE);
199                 g_assert (res != -1);
200
201                 if (start_info->suspend) {
202                         g_assert (SuspendThread (result) != (DWORD)-1);
203                         SetEvent (start_info->suspend_event);
204                 }
205         } else if (start_info->suspend) {
206                 CloseHandle (start_info->suspend_event);
207         }
208         if (out_tid)
209                 *out_tid = thread_id;
210         mono_coop_sem_destroy (&(start_info->registered));
211         g_free (start_info);
212         return result;
213 }
214
215
216 MonoNativeThreadId
217 mono_native_thread_id_get (void)
218 {
219         return GetCurrentThreadId ();
220 }
221
222 gboolean
223 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
224 {
225         return id1 == id2;
226 }
227
228 gboolean
229 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
230 {
231         return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
232 }
233
234 void
235 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
236 {
237         HANDLE handle;
238
239         handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
240         g_assert (handle);
241         ResumeThread (handle);
242         CloseHandle (handle);
243 }
244
245 #if HAVE_DECL___READFSDWORD==0
246 static MONO_ALWAYS_INLINE unsigned long long
247 __readfsdword (unsigned long offset)
248 {
249         unsigned long value;
250         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
251    __asm__ volatile ("movl    %%fs:%1,%0"
252      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
253         return value;
254 }
255 #endif
256
257 void
258 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
259 {
260         MEMORY_BASIC_INFORMATION meminfo;
261 #ifdef _WIN64
262         /* win7 apis */
263         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
264         guint8 *stackTop = (guint8*)tib->StackBase;
265         guint8 *stackBottom = (guint8*)tib->StackLimit;
266 #else
267         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
268         void* tib = (void*)__readfsdword(0x18);
269         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
270         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
271 #endif
272         /*
273         Windows stacks are expanded on demand, one page at time. The TIB reports
274         only the currently allocated amount.
275         VirtualQuery will return the actual limit for the bottom, which is what we want.
276         */
277         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
278                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
279
280         *staddr = stackBottom;
281         *stsize = stackTop - stackBottom;
282
283 }
284
285 gboolean
286 mono_threads_core_yield (void)
287 {
288         return SwitchToThread ();
289 }
290
291 void
292 mono_threads_core_exit (int exit_code)
293 {
294         ExitThread (exit_code);
295 }
296
297 void
298 mono_threads_core_unregister (MonoThreadInfo *info)
299 {
300 }
301
302 HANDLE
303 mono_threads_core_open_handle (void)
304 {
305         HANDLE thread_handle;
306
307         thread_handle = GetCurrentThread ();
308         g_assert (thread_handle);
309
310         /*
311          * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
312          * refer to the thread from other threads for things like aborting.
313          */
314         DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
315                                          THREAD_ALL_ACCESS, TRUE, 0);
316
317         return thread_handle;
318 }
319
320 int
321 mono_threads_get_max_stack_size (void)
322 {
323         //FIXME
324         return INT_MAX;
325 }
326
327 HANDLE
328 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
329 {
330         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
331 }
332
333 #if defined(_MSC_VER)
334 const DWORD MS_VC_EXCEPTION=0x406D1388;
335 #pragma pack(push,8)
336 typedef struct tagTHREADNAME_INFO
337 {
338    DWORD dwType; // Must be 0x1000.
339    LPCSTR szName; // Pointer to name (in user addr space).
340    DWORD dwThreadID; // Thread ID (-1=caller thread).
341   DWORD dwFlags; // Reserved for future use, must be zero.
342 } THREADNAME_INFO;
343 #pragma pack(pop)
344 #endif
345
346 void
347 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
348 {
349 #if defined(_MSC_VER)
350         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
351         THREADNAME_INFO info;
352         info.dwType = 0x1000;
353         info.szName = name;
354         info.dwThreadID = tid;
355         info.dwFlags = 0;
356
357         __try {
358                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
359         }
360         __except(EXCEPTION_EXECUTE_HANDLER) {
361         }
362 #endif
363 }
364
365 #endif