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)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
21 #include <sys/types.h>
24 #include <mono/io-layer/wapi.h>
25 #include <mono/io-layer/wapi-private.h>
26 #include <mono/io-layer/handles-private.h>
27 #include <mono/io-layer/thread-private.h>
28 #include <mono/io-layer/mutex-private.h>
29 #include <mono/io-layer/io-trace.h>
31 #include <mono/utils/mono-threads.h>
32 #include <mono/utils/atomic.h>
33 #include <mono/utils/mono-time.h>
34 #include <mono/utils/mono-once.h>
35 #include <mono/utils/mono-logger-internals.h>
37 #ifdef HAVE_VALGRIND_MEMCHECK_H
38 #include <valgrind/memcheck.h>
41 static void thread_details (gpointer data);
42 static const gchar* thread_typename (void);
43 static gsize thread_typesize (void);
45 struct _WapiHandleOps _wapi_thread_ops = {
50 NULL, /* special_wait */
52 thread_details, /* details */
53 thread_typename, /* typename */
54 thread_typesize, /* typesize */
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);
66 static void thread_details (gpointer data)
68 WapiHandle_thread *thread = (WapiHandle_thread*) data;
69 g_print ("id: %p, owned_mutexes: %d, priority: %d",
70 thread->id, thread->owned_mutexes->len, thread->priority);
73 static const gchar* thread_typename (void)
78 static gsize thread_typesize (void)
80 return sizeof (WapiHandle_thread);
84 _wapi_thread_cleanup (void)
89 get_current_thread_handle (void)
93 info = mono_thread_info_current ();
95 g_assert (info->handle);
99 static WapiHandle_thread*
100 lookup_thread (HANDLE handle)
102 WapiHandle_thread *thread;
105 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
106 (gpointer *)&thread);
111 static WapiHandle_thread*
112 get_current_thread (void)
116 handle = get_current_thread_handle ();
117 return lookup_thread (handle);
121 wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
123 WapiHandle_thread *thread_handle;
125 pid_t pid = _wapi_getpid ();
126 pthread_t tid = pthread_self ();
128 if (_wapi_handle_issignalled (handle) ||
129 _wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
130 /* We must have already deliberately finished with
131 * this thread, so don't do any more now
136 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p terminating", __func__, handle);
138 thread_handle = lookup_thread (handle);
140 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p abandoning held mutexes", __func__, handle);
142 for (i = 0; i < thread_handle->owned_mutexes->len; i++) {
143 gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i);
145 _wapi_mutex_abandon (mutex, pid, tid);
146 _wapi_thread_disown_mutex (mutex);
148 g_ptr_array_free (thread_handle->owned_mutexes, TRUE);
150 thr_ret = _wapi_handle_lock_handle (handle);
151 g_assert (thr_ret == 0);
153 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
155 thr_ret = _wapi_handle_unlock_handle (handle);
156 g_assert (thr_ret == 0);
158 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Recording thread handle %p id %ld status as %d",
159 __func__, handle, thread_handle->id, exitstatus);
161 /* The thread is no longer active, so unref it */
162 _wapi_handle_unref (handle);
166 * wapi_create_thread_handle:
168 * Create a thread handle for the current thread.
171 wapi_create_thread_handle (void)
173 WapiHandle_thread thread_handle = {0}, *thread;
176 mono_once (&thread_ops_once, thread_ops_init);
178 thread_handle.owned_mutexes = g_ptr_array_new ();
180 handle = _wapi_handle_new (WAPI_HANDLE_THREAD, &thread_handle);
181 if (handle == _WAPI_HANDLE_INVALID) {
182 g_warning ("%s: error creating thread handle", __func__);
183 SetLastError (ERROR_GEN_FAILURE);
188 thread = lookup_thread (handle);
190 thread->id = pthread_self ();
193 * Hold a reference while the thread is active, because we use
194 * the handle to store thread exit information
196 _wapi_handle_ref (handle);
198 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: started thread id %ld", __func__, thread->id);
204 wapi_ref_thread_handle (gpointer handle)
206 _wapi_handle_ref (handle);
210 wapi_get_current_thread_handle (void)
212 return get_current_thread_handle ();
216 _wapi_thread_cur_apc_pending (void)
218 return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
222 _wapi_thread_own_mutex (gpointer mutex)
224 WapiHandle_thread *thread;
226 thread = get_current_thread ();
228 _wapi_handle_ref (mutex);
230 g_ptr_array_add (thread->owned_mutexes, mutex);
234 _wapi_thread_disown_mutex (gpointer mutex)
236 WapiHandle_thread *thread;
238 thread = get_current_thread ();
240 _wapi_handle_unref (mutex);
242 g_ptr_array_remove (thread->owned_mutexes, mutex);
246 * wapi_init_thread_info_priority:
247 * @param handle: The thread handle to set.
248 * @param priority: Priority to initialize with
250 * Initialize the priority field of the thread info
253 wapi_init_thread_info_priority (gpointer handle, gint32 priority)
255 struct _WapiHandle_thread *thread_handle = NULL;
256 gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
257 (gpointer *)&thread_handle);
260 thread_handle->priority = priority;
264 * _wapi_thread_posix_priority_to_priority:
266 * Convert a POSIX priority to a WapiThreadPriority.
267 * sched_priority is a POSIX priority,
268 * policy is the current scheduling policy
270 static WapiThreadPriority
271 _wapi_thread_posix_priority_to_priority (int sched_priority, int policy)
273 /* Necessary to get valid priority range */
274 #ifdef _POSIX_PRIORITY_SCHEDULING
280 WapiThreadPriority priorities[] = {
281 THREAD_PRIORITY_LOWEST,
282 THREAD_PRIORITY_LOWEST,
283 THREAD_PRIORITY_BELOW_NORMAL,
284 THREAD_PRIORITY_NORMAL,
285 THREAD_PRIORITY_ABOVE_NORMAL,
286 THREAD_PRIORITY_HIGHEST,
287 THREAD_PRIORITY_HIGHEST
290 max = sched_get_priority_max (policy);
291 min = sched_get_priority_min (policy);
293 /* Partition priority range linearly,
294 assign each partition a thread priority */
295 if (max != min && 0 <= max && 0 <= min) {
296 for (i=1, priority=min, chunk=(max-min)/7;
297 i<6 && sched_priority > priority;
304 return (THREAD_PRIORITY_HIGHEST);
308 return (priorities[i-1]);
313 return (THREAD_PRIORITY_NORMAL);
317 * wapi_thread_priority_to_posix_priority:
319 * Convert a WapiThreadPriority to a POSIX priority.
320 * priority is a WapiThreadPriority,
321 * policy is the current scheduling policy
324 wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
326 /* Necessary to get valid priority range */
327 #ifdef _POSIX_PRIORITY_SCHEDULING
332 WapiThreadPriority priorities[] = {
333 THREAD_PRIORITY_LOWEST,
334 THREAD_PRIORITY_LOWEST,
335 THREAD_PRIORITY_BELOW_NORMAL,
336 THREAD_PRIORITY_NORMAL,
337 THREAD_PRIORITY_ABOVE_NORMAL,
338 THREAD_PRIORITY_HIGHEST,
339 THREAD_PRIORITY_HIGHEST
342 max = sched_get_priority_max (policy);
343 min = sched_get_priority_min (policy);
345 /* Partition priority range linearly,
346 numerically approximate matching ThreadPriority */
347 if (max != min && 0 <= max && 0 <= min) {
348 for (i=0; i<7; ++i) {
349 if (priorities[i] == priority) {
350 posix_priority = min + ((max-min)/7) * i;
351 if (max < posix_priority)
356 return posix_priority;
379 * @param handle: The thread handle to query.
381 * Gets the priority of the given thread.
382 * @return: A MonoThreadPriority approximating the current POSIX
383 * thread priority, or THREAD_PRIORITY_NORMAL on error.
386 GetThreadPriority (gpointer handle)
388 struct _WapiHandle_thread *thread_handle = NULL;
390 struct sched_param param;
391 gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
392 (gpointer *)&thread_handle);
395 return (THREAD_PRIORITY_NORMAL);
397 switch (pthread_getschedparam (thread_handle->id, &policy, ¶m)) {
399 if ((policy == SCHED_FIFO) || (policy == SCHED_RR))
400 return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
402 return (thread_handle->priority);
404 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
407 return (THREAD_PRIORITY_NORMAL);
412 * @param handle: The thread handle to query.
413 * @param priority: The priority to give to the thread.
415 * Sets the priority of the given thread.
416 * @return: TRUE on success, FALSE on failure or error.
419 SetThreadPriority (gpointer handle, gint32 priority)
421 struct _WapiHandle_thread *thread_handle = NULL;
425 struct sched_param param;
426 gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
427 (gpointer *)&thread_handle);
433 rv = pthread_getschedparam (thread_handle->id, &policy, ¶m);
436 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
440 posix_priority = wapi_thread_priority_to_posix_priority (priority, policy);
441 if (0 > posix_priority)
444 param.sched_priority = posix_priority;
445 switch (pthread_setschedparam (thread_handle->id, policy, ¶m)) {
447 thread_handle->priority = priority;
450 g_warning ("pthread_setschedprio: error looking up thread id %x", (gsize)thread_handle->id);
453 g_warning ("%s: priority %d not supported", __func__, priority);
456 g_warning ("%s: permission denied", __func__);
464 wapi_current_thread_desc (void)
466 WapiHandle_thread *thread;
467 gpointer thread_handle;
472 thread_handle = get_current_thread_handle ();
473 thread = lookup_thread (thread_handle);
475 text = g_string_new (0);
476 g_string_append_printf (text, "thread handle %p state : ", thread_handle);
478 mono_thread_info_describe_interrupt_token (mono_thread_info_current (), text);
480 g_string_append_printf (text, " owns (");
481 for (i = 0; i < thread->owned_mutexes->len; i++)
482 g_string_append_printf (text, i > 0 ? ", %p" : "%p", g_ptr_array_index (thread->owned_mutexes, i));
483 g_string_append_printf (text, ")");
486 g_string_free (text, FALSE);