2 * threads.c: Thread handles
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002-2006 Ximian, Inc.
8 * Copyright 2003-2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
20 #include <sys/types.h>
23 #include <mono/io-layer/wapi.h>
24 #include <mono/io-layer/wapi-private.h>
25 #include <mono/io-layer/handles-private.h>
26 #include <mono/io-layer/thread-private.h>
27 #include <mono/io-layer/mutex-private.h>
28 #include <mono/io-layer/io-trace.h>
30 #include <mono/utils/mono-threads.h>
31 #include <mono/utils/atomic.h>
32 #include <mono/utils/mono-time.h>
33 #include <mono/utils/mono-once.h>
34 #include <mono/utils/mono-logger-internals.h>
36 #ifdef HAVE_VALGRIND_MEMCHECK_H
37 #include <valgrind/memcheck.h>
40 struct _WapiHandleOps _wapi_thread_ops = {
45 NULL, /* special_wait */
50 THREAD_PRIORITY_LOWEST = -2,
51 THREAD_PRIORITY_BELOW_NORMAL = -1,
52 THREAD_PRIORITY_NORMAL = 0,
53 THREAD_PRIORITY_ABOVE_NORMAL = 1,
54 THREAD_PRIORITY_HIGHEST = 2
57 static mono_once_t thread_ops_once = MONO_ONCE_INIT;
60 thread_ops_init (void)
62 _wapi_handle_register_capabilities (WAPI_HANDLE_THREAD,
63 WAPI_HANDLE_CAP_WAIT);
67 _wapi_thread_cleanup (void)
72 get_current_thread_handle (void)
76 info = mono_thread_info_current ();
78 g_assert (info->handle);
82 static WapiHandle_thread*
83 lookup_thread (HANDLE handle)
85 WapiHandle_thread *thread;
88 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
94 static WapiHandle_thread*
95 get_current_thread (void)
99 handle = get_current_thread_handle ();
100 return lookup_thread (handle);
104 wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
106 WapiHandle_thread *thread_handle;
108 pid_t pid = _wapi_getpid ();
109 pthread_t tid = pthread_self ();
111 if (_wapi_handle_issignalled (handle) ||
112 _wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
113 /* We must have already deliberately finished with
114 * this thread, so don't do any more now
119 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p terminating", __func__, handle);
121 thread_handle = lookup_thread (handle);
123 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p abandoning held mutexes", __func__, handle);
125 for (i = 0; i < thread_handle->owned_mutexes->len; i++) {
126 gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i);
128 _wapi_mutex_abandon (mutex, pid, tid);
129 _wapi_thread_disown_mutex (mutex);
131 g_ptr_array_free (thread_handle->owned_mutexes, TRUE);
133 thr_ret = _wapi_handle_lock_handle (handle);
134 g_assert (thr_ret == 0);
136 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
138 thr_ret = _wapi_handle_unlock_handle (handle);
139 g_assert (thr_ret == 0);
141 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Recording thread handle %p id %ld status as %d",
142 __func__, handle, thread_handle->id, exitstatus);
144 /* The thread is no longer active, so unref it */
145 _wapi_handle_unref (handle);
149 * wapi_create_thread_handle:
151 * Create a thread handle for the current thread.
154 wapi_create_thread_handle (void)
156 WapiHandle_thread thread_handle = {0}, *thread;
159 mono_once (&thread_ops_once, thread_ops_init);
161 thread_handle.owned_mutexes = g_ptr_array_new ();
163 handle = _wapi_handle_new (WAPI_HANDLE_THREAD, &thread_handle);
164 if (handle == _WAPI_HANDLE_INVALID) {
165 g_warning ("%s: error creating thread handle", __func__);
166 SetLastError (ERROR_GEN_FAILURE);
171 thread = lookup_thread (handle);
173 thread->id = pthread_self ();
176 * Hold a reference while the thread is active, because we use
177 * the handle to store thread exit information
179 _wapi_handle_ref (handle);
181 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: started thread id %ld", __func__, thread->id);
187 wapi_ref_thread_handle (gpointer handle)
189 _wapi_handle_ref (handle);
193 wapi_get_current_thread_handle (void)
195 return get_current_thread_handle ();
199 _wapi_thread_cur_apc_pending (void)
201 return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
205 _wapi_thread_own_mutex (gpointer mutex)
207 WapiHandle_thread *thread;
209 thread = get_current_thread ();
211 _wapi_handle_ref (mutex);
213 g_ptr_array_add (thread->owned_mutexes, mutex);
217 _wapi_thread_disown_mutex (gpointer mutex)
219 WapiHandle_thread *thread;
221 thread = get_current_thread ();
223 _wapi_handle_unref (mutex);
225 g_ptr_array_remove (thread->owned_mutexes, mutex);
229 * _wapi_thread_posix_priority_to_priority:
231 * Convert a POSIX priority to a WapiThreadPriority.
232 * sched_priority is a POSIX priority,
233 * policy is the current scheduling policy
235 static WapiThreadPriority
236 _wapi_thread_posix_priority_to_priority (int sched_priority, int policy)
238 /* Necessary to get valid priority range */
239 #ifdef _POSIX_PRIORITY_SCHEDULING
245 WapiThreadPriority priorities[] = {
246 THREAD_PRIORITY_LOWEST,
247 THREAD_PRIORITY_LOWEST,
248 THREAD_PRIORITY_BELOW_NORMAL,
249 THREAD_PRIORITY_NORMAL,
250 THREAD_PRIORITY_ABOVE_NORMAL,
251 THREAD_PRIORITY_HIGHEST,
252 THREAD_PRIORITY_HIGHEST
255 max = sched_get_priority_max (policy);
256 min = sched_get_priority_min (policy);
258 /* Partition priority range linearly,
259 assign each partition a thread priority */
260 if (max != min && 0 <= max && 0 <= min) {
261 for (i=1, priority=min, chunk=(max-min)/7;
262 i<6 && sched_priority > priority;
269 return (THREAD_PRIORITY_HIGHEST);
273 return (priorities[i-1]);
278 return (THREAD_PRIORITY_NORMAL);
282 * _wapi_thread_priority_to_posix_priority:
284 * Convert a WapiThreadPriority to a POSIX priority.
285 * priority is a WapiThreadPriority,
286 * policy is the current scheduling policy
289 _wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
291 /* Necessary to get valid priority range */
292 #ifdef _POSIX_PRIORITY_SCHEDULING
297 WapiThreadPriority priorities[] = {
298 THREAD_PRIORITY_LOWEST,
299 THREAD_PRIORITY_LOWEST,
300 THREAD_PRIORITY_BELOW_NORMAL,
301 THREAD_PRIORITY_NORMAL,
302 THREAD_PRIORITY_ABOVE_NORMAL,
303 THREAD_PRIORITY_HIGHEST,
304 THREAD_PRIORITY_HIGHEST
307 max = sched_get_priority_max (policy);
308 min = sched_get_priority_min (policy);
310 /* Partition priority range linearly,
311 numerically approximate matching ThreadPriority */
312 if (max != min && 0 <= max && 0 <= min) {
313 for (i=0; i<7; ++i) {
314 if (priorities[i] == priority) {
315 posix_priority = min + ((max-min)/7) * i;
316 if (max < posix_priority)
321 return posix_priority;
344 * @param handle: The thread handle to query.
346 * Gets the priority of the given thread.
347 * @return: A MonoThreadPriority approximating the current POSIX
348 * thread priority, or THREAD_PRIORITY_NORMAL on error.
351 GetThreadPriority (gpointer handle)
353 struct _WapiHandle_thread *thread_handle;
355 struct sched_param param;
356 gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
357 (gpointer *)&thread_handle);
360 return (THREAD_PRIORITY_NORMAL);
363 switch (pthread_getschedparam (thread_handle->id, &policy, ¶m)) {
365 return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
367 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
370 return (THREAD_PRIORITY_NORMAL);
375 * @param handle: The thread handle to query.
376 * @param priority: The priority to give to the thread.
378 * Sets the priority of the given thread.
379 * @return: TRUE on success, FALSE on failure or error.
382 SetThreadPriority (gpointer handle, gint32 priority)
384 struct _WapiHandle_thread *thread_handle;
388 struct sched_param param;
389 gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
390 (gpointer *)&thread_handle);
396 rv = pthread_getschedparam (thread_handle->id, &policy, ¶m);
399 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
403 posix_priority = _wapi_thread_priority_to_posix_priority (priority, policy);
404 if (0 > posix_priority)
407 param.sched_priority = posix_priority;
408 switch (pthread_setschedparam (thread_handle->id, policy, ¶m)) {
412 g_warning ("pthread_setschedparam: error looking up thread id %x", (gsize)thread_handle->id);
415 g_warning ("%s: priority %d not supported", __func__, priority);
418 g_warning ("%s: permission denied", __func__);
426 wapi_current_thread_desc (void)
428 WapiHandle_thread *thread;
429 gpointer thread_handle;
434 thread_handle = get_current_thread_handle ();
435 thread = lookup_thread (thread_handle);
437 text = g_string_new (0);
438 g_string_append_printf (text, "thread handle %p state : ", thread_handle);
440 mono_thread_info_describe_interrupt_token (mono_thread_info_current (), text);
442 g_string_append_printf (text, " owns (");
443 for (i = 0; i < thread->owned_mutexes->len; i++)
444 g_string_append_printf (text, i > 0 ? ", %p" : "%p", g_ptr_array_index (thread->owned_mutexes, i));
445 g_string_append_printf (text, ")");
448 g_string_free (text, FALSE);