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 struct _WapiHandleOps _wapi_thread_ops = {
46 NULL, /* special_wait */
50 static mono_once_t thread_ops_once = MONO_ONCE_INIT;
53 thread_ops_init (void)
55 _wapi_handle_register_capabilities (WAPI_HANDLE_THREAD,
56 WAPI_HANDLE_CAP_WAIT);
60 _wapi_thread_cleanup (void)
65 get_current_thread_handle (void)
69 info = mono_thread_info_current ();
71 g_assert (info->handle);
75 static WapiHandle_thread*
76 lookup_thread (HANDLE handle)
78 WapiHandle_thread *thread;
81 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
87 static WapiHandle_thread*
88 get_current_thread (void)
92 handle = get_current_thread_handle ();
93 return lookup_thread (handle);
97 wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
99 WapiHandle_thread *thread_handle;
101 pid_t pid = _wapi_getpid ();
102 pthread_t tid = pthread_self ();
104 if (_wapi_handle_issignalled (handle) ||
105 _wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
106 /* We must have already deliberately finished with
107 * this thread, so don't do any more now
112 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p terminating", __func__, handle);
114 thread_handle = lookup_thread (handle);
116 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p abandoning held mutexes", __func__, handle);
118 for (i = 0; i < thread_handle->owned_mutexes->len; i++) {
119 gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i);
121 _wapi_mutex_abandon (mutex, pid, tid);
122 _wapi_thread_disown_mutex (mutex);
124 g_ptr_array_free (thread_handle->owned_mutexes, TRUE);
126 thr_ret = _wapi_handle_lock_handle (handle);
127 g_assert (thr_ret == 0);
129 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
131 thr_ret = _wapi_handle_unlock_handle (handle);
132 g_assert (thr_ret == 0);
134 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Recording thread handle %p id %ld status as %d",
135 __func__, handle, thread_handle->id, exitstatus);
137 /* The thread is no longer active, so unref it */
138 _wapi_handle_unref (handle);
142 * wapi_create_thread_handle:
144 * Create a thread handle for the current thread.
147 wapi_create_thread_handle (void)
149 WapiHandle_thread thread_handle = {0}, *thread;
152 mono_once (&thread_ops_once, thread_ops_init);
154 thread_handle.owned_mutexes = g_ptr_array_new ();
156 handle = _wapi_handle_new (WAPI_HANDLE_THREAD, &thread_handle);
157 if (handle == _WAPI_HANDLE_INVALID) {
158 g_warning ("%s: error creating thread handle", __func__);
159 SetLastError (ERROR_GEN_FAILURE);
164 thread = lookup_thread (handle);
166 thread->id = pthread_self ();
169 * Hold a reference while the thread is active, because we use
170 * the handle to store thread exit information
172 _wapi_handle_ref (handle);
174 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: started thread id %ld", __func__, thread->id);
180 wapi_ref_thread_handle (gpointer handle)
182 _wapi_handle_ref (handle);
186 wapi_get_current_thread_handle (void)
188 return get_current_thread_handle ();
192 _wapi_thread_cur_apc_pending (void)
194 return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
198 _wapi_thread_own_mutex (gpointer mutex)
200 WapiHandle_thread *thread;
202 thread = get_current_thread ();
204 _wapi_handle_ref (mutex);
206 g_ptr_array_add (thread->owned_mutexes, mutex);
210 _wapi_thread_disown_mutex (gpointer mutex)
212 WapiHandle_thread *thread;
214 thread = get_current_thread ();
216 _wapi_handle_unref (mutex);
218 g_ptr_array_remove (thread->owned_mutexes, mutex);
222 * wapi_init_thread_info_priority:
223 * @param handle: The thread handle to set.
224 * @param priority: Priority to initialize with
226 * Initialize the priority field of the thread info
229 wapi_init_thread_info_priority (gpointer handle, gint32 priority)
231 struct _WapiHandle_thread *thread_handle = NULL;
232 gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
233 (gpointer *)&thread_handle);
236 thread_handle->priority = priority;
240 * _wapi_thread_posix_priority_to_priority:
242 * Convert a POSIX priority to a WapiThreadPriority.
243 * sched_priority is a POSIX priority,
244 * policy is the current scheduling policy
246 static WapiThreadPriority
247 _wapi_thread_posix_priority_to_priority (int sched_priority, int policy)
249 /* Necessary to get valid priority range */
250 #ifdef _POSIX_PRIORITY_SCHEDULING
256 WapiThreadPriority priorities[] = {
257 THREAD_PRIORITY_LOWEST,
258 THREAD_PRIORITY_LOWEST,
259 THREAD_PRIORITY_BELOW_NORMAL,
260 THREAD_PRIORITY_NORMAL,
261 THREAD_PRIORITY_ABOVE_NORMAL,
262 THREAD_PRIORITY_HIGHEST,
263 THREAD_PRIORITY_HIGHEST
266 max = sched_get_priority_max (policy);
267 min = sched_get_priority_min (policy);
269 /* Partition priority range linearly,
270 assign each partition a thread priority */
271 if (max != min && 0 <= max && 0 <= min) {
272 for (i=1, priority=min, chunk=(max-min)/7;
273 i<6 && sched_priority > priority;
280 return (THREAD_PRIORITY_HIGHEST);
284 return (priorities[i-1]);
289 return (THREAD_PRIORITY_NORMAL);
293 * wapi_thread_priority_to_posix_priority:
295 * Convert a WapiThreadPriority to a POSIX priority.
296 * priority is a WapiThreadPriority,
297 * policy is the current scheduling policy
300 wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
302 /* Necessary to get valid priority range */
303 #ifdef _POSIX_PRIORITY_SCHEDULING
308 WapiThreadPriority priorities[] = {
309 THREAD_PRIORITY_LOWEST,
310 THREAD_PRIORITY_LOWEST,
311 THREAD_PRIORITY_BELOW_NORMAL,
312 THREAD_PRIORITY_NORMAL,
313 THREAD_PRIORITY_ABOVE_NORMAL,
314 THREAD_PRIORITY_HIGHEST,
315 THREAD_PRIORITY_HIGHEST
318 max = sched_get_priority_max (policy);
319 min = sched_get_priority_min (policy);
321 /* Partition priority range linearly,
322 numerically approximate matching ThreadPriority */
323 if (max != min && 0 <= max && 0 <= min) {
324 for (i=0; i<7; ++i) {
325 if (priorities[i] == priority) {
326 posix_priority = min + ((max-min)/7) * i;
327 if (max < posix_priority)
332 return posix_priority;
355 * @param handle: The thread handle to query.
357 * Gets the priority of the given thread.
358 * @return: A MonoThreadPriority approximating the current POSIX
359 * thread priority, or THREAD_PRIORITY_NORMAL on error.
362 GetThreadPriority (gpointer handle)
364 struct _WapiHandle_thread *thread_handle = NULL;
366 struct sched_param param;
367 gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
368 (gpointer *)&thread_handle);
371 return (THREAD_PRIORITY_NORMAL);
373 switch (pthread_getschedparam (thread_handle->id, &policy, ¶m)) {
375 if ((policy == SCHED_FIFO) || (policy == SCHED_RR))
376 return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
378 return (thread_handle->priority);
380 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
383 return (THREAD_PRIORITY_NORMAL);
388 * @param handle: The thread handle to query.
389 * @param priority: The priority to give to the thread.
391 * Sets the priority of the given thread.
392 * @return: TRUE on success, FALSE on failure or error.
395 SetThreadPriority (gpointer handle, gint32 priority)
397 struct _WapiHandle_thread *thread_handle = NULL;
401 struct sched_param param;
402 gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
403 (gpointer *)&thread_handle);
409 rv = pthread_getschedparam (thread_handle->id, &policy, ¶m);
412 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
416 posix_priority = wapi_thread_priority_to_posix_priority (priority, policy);
417 if (0 > posix_priority)
420 param.sched_priority = posix_priority;
421 switch (pthread_setschedparam (thread_handle->id, policy, ¶m)) {
423 thread_handle->priority = priority;
426 g_warning ("pthread_setschedprio: error looking up thread id %x", (gsize)thread_handle->id);
429 g_warning ("%s: priority %d not supported", __func__, priority);
432 g_warning ("%s: permission denied", __func__);
440 wapi_current_thread_desc (void)
442 WapiHandle_thread *thread;
443 gpointer thread_handle;
448 thread_handle = get_current_thread_handle ();
449 thread = lookup_thread (thread_handle);
451 text = g_string_new (0);
452 g_string_append_printf (text, "thread handle %p state : ", thread_handle);
454 mono_thread_info_describe_interrupt_token (mono_thread_info_current (), text);
456 g_string_append_printf (text, " owns (");
457 for (i = 0; i < thread->owned_mutexes->len; i++)
458 g_string_append_printf (text, i > 0 ? ", %p" : "%p", g_ptr_array_index (thread->owned_mutexes, i));
459 g_string_append_printf (text, ")");
462 g_string_free (text, FALSE);