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 */
51 THREAD_PRIORITY_LOWEST = -2,
52 THREAD_PRIORITY_BELOW_NORMAL = -1,
53 THREAD_PRIORITY_NORMAL = 0,
54 THREAD_PRIORITY_ABOVE_NORMAL = 1,
55 THREAD_PRIORITY_HIGHEST = 2
58 static mono_once_t thread_ops_once = MONO_ONCE_INIT;
61 thread_ops_init (void)
63 _wapi_handle_register_capabilities (WAPI_HANDLE_THREAD,
64 WAPI_HANDLE_CAP_WAIT);
68 _wapi_thread_cleanup (void)
73 get_current_thread_handle (void)
77 info = mono_thread_info_current ();
79 g_assert (info->handle);
83 static WapiHandle_thread*
84 lookup_thread (HANDLE handle)
86 WapiHandle_thread *thread;
89 ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
95 static WapiHandle_thread*
96 get_current_thread (void)
100 handle = get_current_thread_handle ();
101 return lookup_thread (handle);
105 wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
107 WapiHandle_thread *thread_handle;
109 pid_t pid = _wapi_getpid ();
110 pthread_t tid = pthread_self ();
112 if (_wapi_handle_issignalled (handle) ||
113 _wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
114 /* We must have already deliberately finished with
115 * this thread, so don't do any more now
120 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p terminating", __func__, handle);
122 thread_handle = lookup_thread (handle);
124 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p abandoning held mutexes", __func__, handle);
126 for (i = 0; i < thread_handle->owned_mutexes->len; i++) {
127 gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i);
129 _wapi_mutex_abandon (mutex, pid, tid);
130 _wapi_thread_disown_mutex (mutex);
132 g_ptr_array_free (thread_handle->owned_mutexes, TRUE);
134 thr_ret = _wapi_handle_lock_handle (handle);
135 g_assert (thr_ret == 0);
137 _wapi_handle_set_signal_state (handle, TRUE, TRUE);
139 thr_ret = _wapi_handle_unlock_handle (handle);
140 g_assert (thr_ret == 0);
142 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Recording thread handle %p id %ld status as %d",
143 __func__, handle, thread_handle->id, exitstatus);
145 /* The thread is no longer active, so unref it */
146 _wapi_handle_unref (handle);
150 * wapi_create_thread_handle:
152 * Create a thread handle for the current thread.
155 wapi_create_thread_handle (void)
157 WapiHandle_thread thread_handle = {0}, *thread;
160 mono_once (&thread_ops_once, thread_ops_init);
162 thread_handle.owned_mutexes = g_ptr_array_new ();
164 handle = _wapi_handle_new (WAPI_HANDLE_THREAD, &thread_handle);
165 if (handle == _WAPI_HANDLE_INVALID) {
166 g_warning ("%s: error creating thread handle", __func__);
167 SetLastError (ERROR_GEN_FAILURE);
172 thread = lookup_thread (handle);
174 thread->id = pthread_self ();
177 * Hold a reference while the thread is active, because we use
178 * the handle to store thread exit information
180 _wapi_handle_ref (handle);
182 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: started thread id %ld", __func__, thread->id);
188 wapi_ref_thread_handle (gpointer handle)
190 _wapi_handle_ref (handle);
194 wapi_get_current_thread_handle (void)
196 return get_current_thread_handle ();
200 _wapi_thread_cur_apc_pending (void)
202 return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
206 _wapi_thread_own_mutex (gpointer mutex)
208 WapiHandle_thread *thread;
210 thread = get_current_thread ();
212 _wapi_handle_ref (mutex);
214 g_ptr_array_add (thread->owned_mutexes, mutex);
218 _wapi_thread_disown_mutex (gpointer mutex)
220 WapiHandle_thread *thread;
222 thread = get_current_thread ();
224 _wapi_handle_unref (mutex);
226 g_ptr_array_remove (thread->owned_mutexes, mutex);
230 * _wapi_thread_posix_priority_to_priority:
232 * Convert a POSIX priority to a WapiThreadPriority.
233 * sched_priority is a POSIX priority,
234 * policy is the current scheduling policy
236 static WapiThreadPriority
237 _wapi_thread_posix_priority_to_priority (int sched_priority, int policy)
239 /* Necessary to get valid priority range */
240 #ifdef _POSIX_PRIORITY_SCHEDULING
246 WapiThreadPriority priorities[] = {
247 THREAD_PRIORITY_LOWEST,
248 THREAD_PRIORITY_LOWEST,
249 THREAD_PRIORITY_BELOW_NORMAL,
250 THREAD_PRIORITY_NORMAL,
251 THREAD_PRIORITY_ABOVE_NORMAL,
252 THREAD_PRIORITY_HIGHEST,
253 THREAD_PRIORITY_HIGHEST
256 max = sched_get_priority_max (policy);
257 min = sched_get_priority_min (policy);
259 /* Partition priority range linearly,
260 assign each partition a thread priority */
261 if (max != min && 0 <= max && 0 <= min) {
262 for (i=1, priority=min, chunk=(max-min)/7;
263 i<6 && sched_priority > priority;
270 return (THREAD_PRIORITY_HIGHEST);
274 return (priorities[i-1]);
279 return (THREAD_PRIORITY_NORMAL);
283 * _wapi_thread_priority_to_posix_priority:
285 * Convert a WapiThreadPriority to a POSIX priority.
286 * priority is a WapiThreadPriority,
287 * policy is the current scheduling policy
290 _wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
292 /* Necessary to get valid priority range */
293 #ifdef _POSIX_PRIORITY_SCHEDULING
298 WapiThreadPriority priorities[] = {
299 THREAD_PRIORITY_LOWEST,
300 THREAD_PRIORITY_LOWEST,
301 THREAD_PRIORITY_BELOW_NORMAL,
302 THREAD_PRIORITY_NORMAL,
303 THREAD_PRIORITY_ABOVE_NORMAL,
304 THREAD_PRIORITY_HIGHEST,
305 THREAD_PRIORITY_HIGHEST
308 max = sched_get_priority_max (policy);
309 min = sched_get_priority_min (policy);
311 /* Partition priority range linearly,
312 numerically approximate matching ThreadPriority */
313 if (max != min && 0 <= max && 0 <= min) {
314 for (i=0; i<7; ++i) {
315 if (priorities[i] == priority) {
316 posix_priority = min + ((max-min)/7) * i;
317 if (max < posix_priority)
322 return posix_priority;
345 * @param handle: The thread handle to query.
347 * Gets the priority of the given thread.
348 * @return: A MonoThreadPriority approximating the current POSIX
349 * thread priority, or THREAD_PRIORITY_NORMAL on error.
352 GetThreadPriority (gpointer handle)
354 struct _WapiHandle_thread *thread_handle;
356 struct sched_param param;
357 gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
358 (gpointer *)&thread_handle);
361 return (THREAD_PRIORITY_NORMAL);
364 switch (pthread_getschedparam (thread_handle->id, &policy, ¶m)) {
366 return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
368 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
371 return (THREAD_PRIORITY_NORMAL);
376 * @param handle: The thread handle to query.
377 * @param priority: The priority to give to the thread.
379 * Sets the priority of the given thread.
380 * @return: TRUE on success, FALSE on failure or error.
383 SetThreadPriority (gpointer handle, gint32 priority)
385 struct _WapiHandle_thread *thread_handle;
389 struct sched_param param;
390 gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
391 (gpointer *)&thread_handle);
397 rv = pthread_getschedparam (thread_handle->id, &policy, ¶m);
400 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
404 posix_priority = _wapi_thread_priority_to_posix_priority (priority, policy);
405 if (0 > posix_priority)
408 param.sched_priority = posix_priority;
409 switch (pthread_setschedparam (thread_handle->id, policy, ¶m)) {
413 g_warning ("pthread_setschedparam: error looking up thread id %x", (gsize)thread_handle->id);
416 g_warning ("%s: priority %d not supported", __func__, priority);
419 g_warning ("%s: permission denied", __func__);
427 wapi_current_thread_desc (void)
429 WapiHandle_thread *thread;
430 gpointer thread_handle;
435 thread_handle = get_current_thread_handle ();
436 thread = lookup_thread (thread_handle);
438 text = g_string_new (0);
439 g_string_append_printf (text, "thread handle %p state : ", thread_handle);
441 mono_thread_info_describe_interrupt_token (mono_thread_info_current (), text);
443 g_string_append_printf (text, " owns (");
444 for (i = 0; i < thread->owned_mutexes->len; i++)
445 g_string_append_printf (text, i > 0 ? ", %p" : "%p", g_ptr_array_index (thread->owned_mutexes, i));
446 g_string_append_printf (text, ")");
449 g_string_free (text, FALSE);