2 * mono-threads-windows.c: Low-level threading, windows version
5 * Rodrigo Kumpera (kumpera@gmail.com)
10 #include <mono/utils/mono-threads.h>
12 #if defined(USE_WINDOWS_BACKEND)
14 #include <mono/utils/mono-compiler.h>
19 mono_threads_init_platform (void)
24 interrupt_apc (ULONG_PTR param
)
29 mono_threads_core_abort_syscall (MonoThreadInfo
*info
)
31 DWORD id
= mono_thread_info_get_tid (info
);
34 handle
= OpenThread (THREAD_ALL_ACCESS
, FALSE
, id
);
37 QueueUserAPC ((PAPCFUNC
)interrupt_apc
, handle
, (ULONG_PTR
)NULL
);
43 mono_threads_core_needs_abort_syscall (void)
49 mono_threads_core_begin_async_suspend (MonoThreadInfo
*info
, gboolean interrupt_kernel
)
51 DWORD id
= mono_thread_info_get_tid (info
);
56 handle
= OpenThread (THREAD_ALL_ACCESS
, FALSE
, id
);
59 result
= SuspendThread (handle
);
60 THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id
, ret
);
61 if (result
== (DWORD
)-1) {
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);
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
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
);
79 //FIXME do we need to QueueUserAPC on this case?
81 QueueUserAPC ((PAPCFUNC
)interrupt_apc
, handle
, (ULONG_PTR
)NULL
);
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);
94 mono_threads_core_check_suspend_result (MonoThreadInfo
*info
)
100 mono_threads_core_begin_async_resume (MonoThreadInfo
*info
)
102 DWORD id
= mono_thread_info_get_tid (info
);
106 handle
= OpenThread (THREAD_ALL_ACCESS
, FALSE
, id
);
109 if (info
->async_target
) {
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
);
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
);
133 CloseHandle (handle
);
138 result
= ResumeThread (handle
);
139 CloseHandle (handle
);
141 return result
!= (DWORD
)-1;
146 mono_threads_platform_register (MonoThreadInfo
*info
)
151 mono_threads_platform_free (MonoThreadInfo
*info
)
157 #if defined (HOST_WIN32)
160 LPTHREAD_START_ROUTINE start_routine
;
162 MonoSemType registered
;
164 HANDLE suspend_event
;
168 inner_start_thread (LPVOID arg
)
170 ThreadStartInfo
*start_info
= arg
;
171 void *t_arg
= start_info
->arg
;
173 LPTHREAD_START_ROUTINE start_func
= start_info
->start_routine
;
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
);
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 ();
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
;
205 start_info
= g_malloc0 (sizeof (ThreadStartInfo
));
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
)
219 result
= CreateThread (NULL
, stack_size
, inner_start_thread
, start_info
, creation_flags
, &thread_id
);
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
);
232 *out_tid
= thread_id
;
233 MONO_SEM_DESTROY (&(start_info
->registered
));
240 mono_native_thread_id_get (void)
242 return GetCurrentThreadId ();
246 mono_native_thread_id_equals (MonoNativeThreadId id1
, MonoNativeThreadId id2
)
252 mono_native_thread_create (MonoNativeThreadId
*tid
, gpointer func
, gpointer arg
)
254 return CreateThread (NULL
, 0, (func
), (arg
), 0, (tid
)) != NULL
;
258 mono_threads_core_resume_created (MonoThreadInfo
*info
, MonoNativeThreadId tid
)
262 handle
= OpenThread (THREAD_ALL_ACCESS
, TRUE
, tid
);
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
)
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
)));
281 mono_threads_core_get_stack_bounds (guint8
**staddr
, size_t *stsize
)
283 MEMORY_BASIC_INFORMATION meminfo
;
286 NT_TIB
* tib
= (NT_TIB
*)NtCurrentTeb();
287 guint8
*stackTop
= (guint8
*)tib
->StackBase
;
288 guint8
*stackBottom
= (guint8
*)tib
->StackLimit
;
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);
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
;
309 mono_threads_core_yield (void)
311 return SwitchToThread ();
315 mono_threads_core_exit (int exit_code
)
317 ExitThread (exit_code
);
321 mono_threads_core_unregister (MonoThreadInfo
*info
)
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)
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;
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.
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;
377 info
.dwThreadID
= tid
;
381 RaiseException( MS_VC_EXCEPTION
, 0, sizeof(info
)/sizeof(ULONG_PTR
), (ULONG_PTR
*)&info
);
383 __except(EXCEPTION_EXECUTE_HANDLER
) {
390 mono_threads_core_prepare_interrupt (HANDLE thread_handle
)
396 mono_threads_core_finish_interrupt (gpointer wait_handle
)
401 mono_threads_core_self_interrupt (void)
406 mono_threads_core_clear_interruption (void)
411 mono_threads_core_begin_global_suspend (void)
416 mono_threads_core_end_global_suspend (void)