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/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>
35 #include <mono/utils/w32handle.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 static MonoW32HandleOps _wapi_thread_ops = {
50 NULL, /* special_wait */
52 thread_details, /* details */
53 thread_typename, /* typename */
54 thread_typesize, /* typesize */
58 _wapi_thread_init (void)
60 mono_w32handle_register_ops (MONO_W32HANDLE_THREAD, &_wapi_thread_ops);
62 mono_w32handle_register_capabilities (MONO_W32HANDLE_THREAD, MONO_W32HANDLE_CAP_WAIT);
65 static void thread_details (gpointer data)
67 WapiHandle_thread *thread = (WapiHandle_thread*) data;
68 g_print ("id: %p, owned_mutexes: %d, priority: %d",
69 thread->id, thread->owned_mutexes->len, thread->priority);
72 static const gchar* thread_typename (void)
77 static gsize thread_typesize (void)
79 return sizeof (WapiHandle_thread);
83 _wapi_thread_cleanup (void)
88 get_current_thread_handle (void)
92 info = mono_thread_info_current ();
94 g_assert (info->handle);
98 static WapiHandle_thread*
99 lookup_thread (HANDLE handle)
101 WapiHandle_thread *thread;
104 ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_THREAD,
105 (gpointer *)&thread);
110 static WapiHandle_thread*
111 get_current_thread (void)
115 handle = get_current_thread_handle ();
116 return lookup_thread (handle);
120 wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
122 WapiHandle_thread *thread_handle;
124 pid_t pid = _wapi_getpid ();
125 pthread_t tid = pthread_self ();
127 if (mono_w32handle_issignalled (handle) ||
128 mono_w32handle_get_type (handle) == MONO_W32HANDLE_UNUSED) {
129 /* We must have already deliberately finished with
130 * this thread, so don't do any more now
135 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p terminating", __func__, handle);
137 thread_handle = lookup_thread (handle);
139 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p abandoning held mutexes", __func__, handle);
141 for (i = 0; i < thread_handle->owned_mutexes->len; i++) {
142 gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i);
144 _wapi_mutex_abandon (mutex, pid, tid);
145 _wapi_thread_disown_mutex (mutex);
147 g_ptr_array_free (thread_handle->owned_mutexes, TRUE);
149 thr_ret = mono_w32handle_lock_handle (handle);
150 g_assert (thr_ret == 0);
152 mono_w32handle_set_signal_state (handle, TRUE, TRUE);
154 thr_ret = mono_w32handle_unlock_handle (handle);
155 g_assert (thr_ret == 0);
157 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Recording thread handle %p id %ld status as %d",
158 __func__, handle, thread_handle->id, exitstatus);
160 /* The thread is no longer active, so unref it */
161 mono_w32handle_unref (handle);
165 * wapi_create_thread_handle:
167 * Create a thread handle for the current thread.
170 wapi_create_thread_handle (void)
172 WapiHandle_thread thread_handle = {0}, *thread;
175 thread_handle.owned_mutexes = g_ptr_array_new ();
177 handle = mono_w32handle_new (MONO_W32HANDLE_THREAD, &thread_handle);
178 if (handle == INVALID_HANDLE_VALUE) {
179 g_warning ("%s: error creating thread handle", __func__);
180 SetLastError (ERROR_GEN_FAILURE);
185 thread = lookup_thread (handle);
187 thread->id = pthread_self ();
190 * Hold a reference while the thread is active, because we use
191 * the handle to store thread exit information
193 mono_w32handle_ref (handle);
195 MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: started thread id %ld", __func__, thread->id);
201 wapi_ref_thread_handle (gpointer handle)
203 mono_w32handle_ref (handle);
207 wapi_get_current_thread_handle (void)
209 return get_current_thread_handle ();
213 _wapi_thread_cur_apc_pending (void)
215 return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
219 _wapi_thread_own_mutex (gpointer mutex)
221 WapiHandle_thread *thread;
223 thread = get_current_thread ();
225 mono_w32handle_ref (mutex);
227 g_ptr_array_add (thread->owned_mutexes, mutex);
231 _wapi_thread_disown_mutex (gpointer mutex)
233 WapiHandle_thread *thread;
235 thread = get_current_thread ();
237 mono_w32handle_unref (mutex);
239 g_ptr_array_remove (thread->owned_mutexes, mutex);
243 * wapi_init_thread_info_priority:
244 * @param handle: The thread handle to set.
245 * @param priority: Priority to initialize with
247 * Initialize the priority field of the thread info
250 wapi_init_thread_info_priority (gpointer handle, gint32 priority)
252 struct _WapiHandle_thread *thread_handle = NULL;
253 gboolean ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_THREAD,
254 (gpointer *)&thread_handle);
257 thread_handle->priority = priority;
261 * _wapi_thread_posix_priority_to_priority:
263 * Convert a POSIX priority to a WapiThreadPriority.
264 * sched_priority is a POSIX priority,
265 * policy is the current scheduling policy
267 static WapiThreadPriority
268 _wapi_thread_posix_priority_to_priority (int sched_priority, int policy)
270 /* Necessary to get valid priority range */
271 #ifdef _POSIX_PRIORITY_SCHEDULING
277 WapiThreadPriority priorities[] = {
278 THREAD_PRIORITY_LOWEST,
279 THREAD_PRIORITY_LOWEST,
280 THREAD_PRIORITY_BELOW_NORMAL,
281 THREAD_PRIORITY_NORMAL,
282 THREAD_PRIORITY_ABOVE_NORMAL,
283 THREAD_PRIORITY_HIGHEST,
284 THREAD_PRIORITY_HIGHEST
287 max = sched_get_priority_max (policy);
288 min = sched_get_priority_min (policy);
290 /* Partition priority range linearly,
291 assign each partition a thread priority */
292 if (max != min && 0 <= max && 0 <= min) {
293 for (i=1, priority=min, chunk=(max-min)/7;
294 i<6 && sched_priority > priority;
301 return (THREAD_PRIORITY_HIGHEST);
305 return (priorities[i-1]);
310 return (THREAD_PRIORITY_NORMAL);
314 * wapi_thread_priority_to_posix_priority:
316 * Convert a WapiThreadPriority to a POSIX priority.
317 * priority is a WapiThreadPriority,
318 * policy is the current scheduling policy
321 wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
323 /* Necessary to get valid priority range */
324 #ifdef _POSIX_PRIORITY_SCHEDULING
329 WapiThreadPriority priorities[] = {
330 THREAD_PRIORITY_LOWEST,
331 THREAD_PRIORITY_LOWEST,
332 THREAD_PRIORITY_BELOW_NORMAL,
333 THREAD_PRIORITY_NORMAL,
334 THREAD_PRIORITY_ABOVE_NORMAL,
335 THREAD_PRIORITY_HIGHEST,
336 THREAD_PRIORITY_HIGHEST
339 max = sched_get_priority_max (policy);
340 min = sched_get_priority_min (policy);
342 /* Partition priority range linearly,
343 numerically approximate matching ThreadPriority */
344 if (max != min && 0 <= max && 0 <= min) {
345 for (i=0; i<7; ++i) {
346 if (priorities[i] == priority) {
347 posix_priority = min + ((max-min)/7) * i;
348 if (max < posix_priority)
353 return posix_priority;
376 * @param handle: The thread handle to query.
378 * Gets the priority of the given thread.
379 * @return: A MonoThreadPriority approximating the current POSIX
380 * thread priority, or THREAD_PRIORITY_NORMAL on error.
383 GetThreadPriority (gpointer handle)
385 struct _WapiHandle_thread *thread_handle = NULL;
387 struct sched_param param;
388 gboolean ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_THREAD,
389 (gpointer *)&thread_handle);
392 return (THREAD_PRIORITY_NORMAL);
394 switch (pthread_getschedparam (thread_handle->id, &policy, ¶m)) {
396 if ((policy == SCHED_FIFO) || (policy == SCHED_RR))
397 return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
399 return (thread_handle->priority);
401 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
404 return (THREAD_PRIORITY_NORMAL);
409 * @param handle: The thread handle to query.
410 * @param priority: The priority to give to the thread.
412 * Sets the priority of the given thread.
413 * @return: TRUE on success, FALSE on failure or error.
416 SetThreadPriority (gpointer handle, gint32 priority)
418 struct _WapiHandle_thread *thread_handle = NULL;
422 struct sched_param param;
423 gboolean ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_THREAD,
424 (gpointer *)&thread_handle);
430 rv = pthread_getschedparam (thread_handle->id, &policy, ¶m);
433 g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
437 posix_priority = wapi_thread_priority_to_posix_priority (priority, policy);
438 if (0 > posix_priority)
441 param.sched_priority = posix_priority;
442 switch (pthread_setschedparam (thread_handle->id, policy, ¶m)) {
444 thread_handle->priority = priority;
447 g_warning ("pthread_setschedprio: error looking up thread id %x", (gsize)thread_handle->id);
450 g_warning ("%s: priority %d not supported", __func__, priority);
453 g_warning ("%s: permission denied", __func__);
461 wapi_current_thread_desc (void)
463 WapiHandle_thread *thread;
464 gpointer thread_handle;
469 thread_handle = get_current_thread_handle ();
470 thread = lookup_thread (thread_handle);
472 text = g_string_new (0);
473 g_string_append_printf (text, "thread handle %p state : ", thread_handle);
475 mono_thread_info_describe_interrupt_token (mono_thread_info_current (), text);
477 g_string_append_printf (text, " owns (");
478 for (i = 0; i < thread->owned_mutexes->len; i++)
479 g_string_append_printf (text, i > 0 ? ", %p" : "%p", g_ptr_array_index (thread->owned_mutexes, i));
480 g_string_append_printf (text, ")");
483 g_string_free (text, FALSE);