[utils/threads] Add and export a mono_native_thread_join () function.
[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 gboolean
191 mono_native_thread_join (MonoNativeThreadId tid)
192 {
193         HANDLE handle;
194
195         if (!(handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid)))
196                 return FALSE;
197
198         DWORD res = WaitForSingleObject (handle, INFINITE);
199
200         CloseHandle (handle);
201
202         return res != WAIT_FAILED;
203 }
204
205 #if HAVE_DECL___READFSDWORD==0
206 static MONO_ALWAYS_INLINE unsigned long long
207 __readfsdword (unsigned long offset)
208 {
209         unsigned long value;
210         //      __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
211    __asm__ volatile ("movl    %%fs:%1,%0"
212      : "=r" (value) ,"=m" ((*(volatile long *) offset)));
213         return value;
214 }
215 #endif
216
217 void
218 mono_threads_platform_get_stack_bounds (guint8 **staddr, size_t *stsize)
219 {
220         MEMORY_BASIC_INFORMATION meminfo;
221 #ifdef _WIN64
222         /* win7 apis */
223         NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
224         guint8 *stackTop = (guint8*)tib->StackBase;
225         guint8 *stackBottom = (guint8*)tib->StackLimit;
226 #else
227         /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
228         void* tib = (void*)__readfsdword(0x18);
229         guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
230         guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
231 #endif
232         /*
233         Windows stacks are expanded on demand, one page at time. The TIB reports
234         only the currently allocated amount.
235         VirtualQuery will return the actual limit for the bottom, which is what we want.
236         */
237         if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
238                 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
239
240         *staddr = stackBottom;
241         *stsize = stackTop - stackBottom;
242
243 }
244
245 gboolean
246 mono_threads_platform_yield (void)
247 {
248         return SwitchToThread ();
249 }
250
251 void
252 mono_threads_platform_exit (int exit_code)
253 {
254         mono_thread_info_detach ();
255         ExitThread (exit_code);
256 }
257
258 void
259 mono_threads_platform_unregister (MonoThreadInfo *info)
260 {
261         g_assert (info->handle);
262
263         CloseHandle (info->handle);
264         info->handle = NULL;
265 }
266
267 int
268 mono_threads_get_max_stack_size (void)
269 {
270         //FIXME
271         return INT_MAX;
272 }
273
274 gpointer
275 mono_threads_platform_duplicate_handle (MonoThreadInfo *info)
276 {
277         HANDLE thread_handle;
278
279         g_assert (info->handle);
280         DuplicateHandle (GetCurrentProcess (), info->handle, GetCurrentProcess (), &thread_handle, THREAD_ALL_ACCESS, TRUE, 0);
281
282         return thread_handle;
283 }
284
285 HANDLE
286 mono_threads_platform_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
287 {
288         return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
289 }
290
291 void
292 mono_threads_platform_close_thread_handle (HANDLE handle)
293 {
294         CloseHandle (handle);
295 }
296
297 #if defined(_MSC_VER)
298 const DWORD MS_VC_EXCEPTION=0x406D1388;
299 #pragma pack(push,8)
300 typedef struct tagTHREADNAME_INFO
301 {
302    DWORD dwType; // Must be 0x1000.
303    LPCSTR szName; // Pointer to name (in user addr space).
304    DWORD dwThreadID; // Thread ID (-1=caller thread).
305   DWORD dwFlags; // Reserved for future use, must be zero.
306 } THREADNAME_INFO;
307 #pragma pack(pop)
308 #endif
309
310 void
311 mono_native_thread_set_name (MonoNativeThreadId tid, const char *name)
312 {
313 #if defined(_MSC_VER)
314         /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
315         THREADNAME_INFO info;
316         info.dwType = 0x1000;
317         info.szName = name;
318         info.dwThreadID = tid;
319         info.dwFlags = 0;
320
321         __try {
322                 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR),       (ULONG_PTR*)&info );
323         }
324         __except(EXCEPTION_EXECUTE_HANDLER) {
325         }
326 #endif
327 }
328
329 void
330 mono_threads_platform_set_exited (gpointer handle)
331 {
332 }
333
334 void
335 mono_threads_platform_init (void)
336 {
337 }
338
339 #endif