[threading] Add a global variable that is set while a global suspend is hapening.
[mono-project.git] / mono / utils / mono-threads-windows.c
blobb5008b4859eb3ae099d0e0631792b58934c525bb
1 /*
2 * mono-threads-windows.c: Low-level threading, windows version
4 * Author:
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * (C) 2011 Novell, Inc
8 */
10 #include <mono/utils/mono-threads.h>
12 #if defined(USE_WINDOWS_BACKEND)
14 #include <mono/utils/mono-compiler.h>
15 #include <limits.h>
18 void
19 mono_threads_init_platform (void)
23 static void CALLBACK
24 interrupt_apc (ULONG_PTR param)
28 void
29 mono_threads_core_abort_syscall (MonoThreadInfo *info)
31 DWORD id = mono_thread_info_get_tid (info);
32 HANDLE handle;
34 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
35 g_assert (handle);
37 QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
39 CloseHandle (handle);
42 gboolean
43 mono_threads_core_needs_abort_syscall (void)
45 return TRUE;
48 gboolean
49 mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
51 DWORD id = mono_thread_info_get_tid (info);
52 HANDLE handle;
53 DWORD result;
54 gboolean res;
56 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
57 g_assert (handle);
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;
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;
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);
89 CloseHandle (handle);
90 return res;
93 gboolean
94 mono_threads_core_check_suspend_result (MonoThreadInfo *info)
96 return TRUE;
99 gboolean
100 mono_threads_core_begin_async_resume (MonoThreadInfo *info)
102 DWORD id = mono_thread_info_get_tid (info);
103 HANDLE handle;
104 DWORD result;
106 handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
107 g_assert (handle);
109 if (info->async_target) {
110 MonoContext ctx;
111 CONTEXT context;
112 gboolean res;
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;
118 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
120 if (!GetThreadContext (handle, &context)) {
121 CloseHandle (handle);
122 return FALSE;
125 g_assert (context.ContextFlags & CONTEXT_INTEGER);
126 g_assert (context.ContextFlags & CONTEXT_CONTROL);
128 mono_monoctx_to_sigctx (&ctx, &context);
130 context.ContextFlags = CONTEXT_INTEGER | CONTEXT_CONTROL;
131 res = SetThreadContext (handle, &context);
132 if (!res) {
133 CloseHandle (handle);
134 return FALSE;
138 result = ResumeThread (handle);
139 CloseHandle (handle);
141 return result != (DWORD)-1;
145 void
146 mono_threads_platform_register (MonoThreadInfo *info)
150 void
151 mono_threads_platform_free (MonoThreadInfo *info)
155 #endif
157 #if defined (HOST_WIN32)
159 typedef struct {
160 LPTHREAD_START_ROUTINE start_routine;
161 void *arg;
162 MonoSemType registered;
163 gboolean suspend;
164 HANDLE suspend_event;
165 } ThreadStartInfo;
167 static DWORD WINAPI
168 inner_start_thread (LPVOID arg)
170 ThreadStartInfo *start_info = arg;
171 void *t_arg = start_info->arg;
172 int post_result;
173 LPTHREAD_START_ROUTINE start_func = start_info->start_routine;
174 DWORD result;
175 gboolean suspend = start_info->suspend;
176 HANDLE suspend_event = start_info->suspend_event;
177 MonoThreadInfo *info;
179 info = mono_thread_info_attach (&result);
180 info->runtime_thread = TRUE;
181 info->create_suspended = suspend;
183 post_result = MONO_SEM_POST (&(start_info->registered));
184 g_assert (!post_result);
186 if (suspend) {
187 WaitForSingleObject (suspend_event, INFINITE); /* caller will suspend the thread before setting the event. */
188 CloseHandle (suspend_event);
191 result = start_func (t_arg);
193 mono_thread_info_detach ();
195 return result;
198 HANDLE
199 mono_threads_core_create_thread (LPTHREAD_START_ROUTINE start_routine, gpointer arg, guint32 stack_size, guint32 creation_flags, MonoNativeThreadId *out_tid)
201 ThreadStartInfo *start_info;
202 HANDLE result;
203 DWORD thread_id;
205 start_info = g_malloc0 (sizeof (ThreadStartInfo));
206 if (!start_info)
207 return NULL;
208 MONO_SEM_INIT (&(start_info->registered), 0);
209 start_info->arg = arg;
210 start_info->start_routine = start_routine;
211 start_info->suspend = creation_flags & CREATE_SUSPENDED;
212 creation_flags &= ~CREATE_SUSPENDED;
213 if (start_info->suspend) {
214 start_info->suspend_event = CreateEvent (NULL, TRUE, FALSE, NULL);
215 if (!start_info->suspend_event)
216 return NULL;
219 result = CreateThread (NULL, stack_size, inner_start_thread, start_info, creation_flags, &thread_id);
220 if (result) {
221 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
222 /*if (EINTR != errno) ABORT("sem_wait failed"); */
224 if (start_info->suspend) {
225 g_assert (SuspendThread (result) != (DWORD)-1);
226 SetEvent (start_info->suspend_event);
228 } else if (start_info->suspend) {
229 CloseHandle (start_info->suspend_event);
231 if (out_tid)
232 *out_tid = thread_id;
233 MONO_SEM_DESTROY (&(start_info->registered));
234 g_free (start_info);
235 return result;
239 MonoNativeThreadId
240 mono_native_thread_id_get (void)
242 return GetCurrentThreadId ();
245 gboolean
246 mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2)
248 return id1 == id2;
251 gboolean
252 mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg)
254 return CreateThread (NULL, 0, (func), (arg), 0, (tid)) != NULL;
257 void
258 mono_threads_core_resume_created (MonoThreadInfo *info, MonoNativeThreadId tid)
260 HANDLE handle;
262 handle = OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
263 g_assert (handle);
264 ResumeThread (handle);
265 CloseHandle (handle);
268 #if HAVE_DECL___READFSDWORD==0
269 static MONO_ALWAYS_INLINE unsigned long long
270 __readfsdword (unsigned long offset)
272 unsigned long value;
273 // __asm__("movl %%fs:%a[offset], %k[value]" : [value] "=q" (value) : [offset] "irm" (offset));
274 __asm__ volatile ("movl %%fs:%1,%0"
275 : "=r" (value) ,"=m" ((*(volatile long *) offset)));
276 return value;
278 #endif
280 void
281 mono_threads_core_get_stack_bounds (guint8 **staddr, size_t *stsize)
283 MEMORY_BASIC_INFORMATION meminfo;
284 #ifdef _WIN64
285 /* win7 apis */
286 NT_TIB* tib = (NT_TIB*)NtCurrentTeb();
287 guint8 *stackTop = (guint8*)tib->StackBase;
288 guint8 *stackBottom = (guint8*)tib->StackLimit;
289 #else
290 /* http://en.wikipedia.org/wiki/Win32_Thread_Information_Block */
291 void* tib = (void*)__readfsdword(0x18);
292 guint8 *stackTop = (guint8*)*(int*)((char*)tib + 4);
293 guint8 *stackBottom = (guint8*)*(int*)((char*)tib + 8);
294 #endif
296 Windows stacks are expanded on demand, one page at time. The TIB reports
297 only the currently allocated amount.
298 VirtualQuery will return the actual limit for the bottom, which is what we want.
300 if (VirtualQuery (&meminfo, &meminfo, sizeof (meminfo)) == sizeof (meminfo))
301 stackBottom = MIN (stackBottom, (guint8*)meminfo.AllocationBase);
303 *staddr = stackBottom;
304 *stsize = stackTop - stackBottom;
308 gboolean
309 mono_threads_core_yield (void)
311 return SwitchToThread ();
314 void
315 mono_threads_core_exit (int exit_code)
317 ExitThread (exit_code);
320 void
321 mono_threads_core_unregister (MonoThreadInfo *info)
325 HANDLE
326 mono_threads_core_open_handle (void)
328 HANDLE thread_handle;
330 thread_handle = GetCurrentThread ();
331 g_assert (thread_handle);
334 * The handle returned by GetCurrentThread () is a pseudo handle, so it can't be used to
335 * refer to the thread from other threads for things like aborting.
337 DuplicateHandle (GetCurrentProcess (), thread_handle, GetCurrentProcess (), &thread_handle,
338 THREAD_ALL_ACCESS, TRUE, 0);
340 return thread_handle;
344 mono_threads_get_max_stack_size (void)
346 //FIXME
347 return INT_MAX;
350 HANDLE
351 mono_threads_core_open_thread_handle (HANDLE handle, MonoNativeThreadId tid)
353 return OpenThread (THREAD_ALL_ACCESS, TRUE, tid);
356 #if defined(_MSC_VER)
357 const DWORD MS_VC_EXCEPTION=0x406D1388;
358 #pragma pack(push,8)
359 typedef struct tagTHREADNAME_INFO
361 DWORD dwType; // Must be 0x1000.
362 LPCSTR szName; // Pointer to name (in user addr space).
363 DWORD dwThreadID; // Thread ID (-1=caller thread).
364 DWORD dwFlags; // Reserved for future use, must be zero.
365 } THREADNAME_INFO;
366 #pragma pack(pop)
367 #endif
369 void
370 mono_threads_core_set_name (MonoNativeThreadId tid, const char *name)
372 #if defined(_MSC_VER)
373 /* http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
374 THREADNAME_INFO info;
375 info.dwType = 0x1000;
376 info.szName = name;
377 info.dwThreadID = tid;
378 info.dwFlags = 0;
380 __try {
381 RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
383 __except(EXCEPTION_EXECUTE_HANDLER) {
385 #endif
389 gpointer
390 mono_threads_core_prepare_interrupt (HANDLE thread_handle)
392 return NULL;
395 void
396 mono_threads_core_finish_interrupt (gpointer wait_handle)
400 void
401 mono_threads_core_self_interrupt (void)
405 void
406 mono_threads_core_clear_interruption (void)
410 void
411 mono_threads_core_begin_global_suspend (void)
415 void
416 mono_threads_core_end_global_suspend (void)
420 #endif