[io-layer] Add typename and typesize handle operations
[mono.git] / mono / io-layer / wthreads.c
1 /*
2  * threads.c:  Thread handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
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.
11  */
12
13 #include <config.h>
14 #include <stdio.h>
15 #include <glib.h>
16 #include <string.h>
17 #include <pthread.h>
18 #include <sched.h>
19 #include <sys/time.h>
20 #include <errno.h>
21 #include <sys/types.h>
22 #include <unistd.h>
23
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>
30
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>
36
37 #ifdef HAVE_VALGRIND_MEMCHECK_H
38 #include <valgrind/memcheck.h>
39 #endif
40
41 static void thread_details (gpointer data);
42 static const gchar* thread_typename (void);
43 static gsize thread_typesize (void);
44
45 struct _WapiHandleOps _wapi_thread_ops = {
46         NULL,                           /* close */
47         NULL,                           /* signal */
48         NULL,                           /* own */
49         NULL,                           /* is_owned */
50         NULL,                           /* special_wait */
51         NULL,                           /* prewait */
52         thread_details,         /* details */
53         thread_typename,        /* typename */
54         thread_typesize,        /* typesize */
55 };
56
57 static mono_once_t thread_ops_once = MONO_ONCE_INIT;
58
59 static void
60 thread_ops_init (void)
61 {
62         _wapi_handle_register_capabilities (WAPI_HANDLE_THREAD,
63                                             WAPI_HANDLE_CAP_WAIT);
64 }
65
66 static void thread_details (gpointer data)
67 {
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);
71 }
72
73 static const gchar* thread_typename (void)
74 {
75         return "Thread";
76 }
77
78 static gsize thread_typesize (void)
79 {
80         return sizeof (WapiHandle_thread);
81 }
82
83 void
84 _wapi_thread_cleanup (void)
85 {
86 }
87
88 static gpointer
89 get_current_thread_handle (void)
90 {
91         MonoThreadInfo *info;
92
93         info = mono_thread_info_current ();
94         g_assert (info);
95         g_assert (info->handle);
96         return info->handle;
97 }
98
99 static WapiHandle_thread*
100 lookup_thread (HANDLE handle)
101 {
102         WapiHandle_thread *thread;
103         gboolean ok;
104
105         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
106                                                           (gpointer *)&thread);
107         g_assert (ok);
108         return thread;
109 }
110
111 static WapiHandle_thread*
112 get_current_thread (void)
113 {
114         gpointer handle;
115
116         handle = get_current_thread_handle ();
117         return lookup_thread (handle);
118 }
119
120 void
121 wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
122 {
123         WapiHandle_thread *thread_handle;
124         int i, thr_ret;
125         pid_t pid = _wapi_getpid ();
126         pthread_t tid = pthread_self ();
127         
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
132                  */
133                 return;
134         }
135
136         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p terminating", __func__, handle);
137
138         thread_handle = lookup_thread (handle);
139
140         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p abandoning held mutexes", __func__, handle);
141
142         for (i = 0; i < thread_handle->owned_mutexes->len; i++) {
143                 gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i);
144
145                 _wapi_mutex_abandon (mutex, pid, tid);
146                 _wapi_thread_disown_mutex (mutex);
147         }
148         g_ptr_array_free (thread_handle->owned_mutexes, TRUE);
149         
150         thr_ret = _wapi_handle_lock_handle (handle);
151         g_assert (thr_ret == 0);
152
153         _wapi_handle_set_signal_state (handle, TRUE, TRUE);
154
155         thr_ret = _wapi_handle_unlock_handle (handle);
156         g_assert (thr_ret == 0);
157         
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);
160         
161         /* The thread is no longer active, so unref it */
162         _wapi_handle_unref (handle);
163 }
164
165 /*
166  * wapi_create_thread_handle:
167  *
168  *   Create a thread handle for the current thread.
169  */
170 gpointer
171 wapi_create_thread_handle (void)
172 {
173         WapiHandle_thread thread_handle = {0}, *thread;
174         gpointer handle;
175
176         mono_once (&thread_ops_once, thread_ops_init);
177
178         thread_handle.owned_mutexes = g_ptr_array_new ();
179
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);
184                 
185                 return NULL;
186         }
187
188         thread = lookup_thread (handle);
189
190         thread->id = pthread_self ();
191
192         /*
193          * Hold a reference while the thread is active, because we use
194          * the handle to store thread exit information
195          */
196         _wapi_handle_ref (handle);
197
198         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: started thread id %ld", __func__, thread->id);
199         
200         return handle;
201 }
202
203 void
204 wapi_ref_thread_handle (gpointer handle)
205 {
206         _wapi_handle_ref (handle);
207 }
208
209 gpointer
210 wapi_get_current_thread_handle (void)
211 {
212         return get_current_thread_handle ();
213 }
214
215 gboolean
216 _wapi_thread_cur_apc_pending (void)
217 {
218         return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
219 }
220
221 void
222 _wapi_thread_own_mutex (gpointer mutex)
223 {
224         WapiHandle_thread *thread;
225         
226         thread = get_current_thread ();
227
228         _wapi_handle_ref (mutex);
229         
230         g_ptr_array_add (thread->owned_mutexes, mutex);
231 }
232
233 void
234 _wapi_thread_disown_mutex (gpointer mutex)
235 {
236         WapiHandle_thread *thread;
237
238         thread = get_current_thread ();
239
240         _wapi_handle_unref (mutex);
241         
242         g_ptr_array_remove (thread->owned_mutexes, mutex);
243 }
244
245 /**
246  * wapi_init_thread_info_priority:
247  * @param handle: The thread handle to set.
248  * @param priority: Priority to initialize with
249  *
250  *   Initialize the priority field of the thread info
251  */
252 void
253 wapi_init_thread_info_priority (gpointer handle, gint32 priority)
254 {
255         struct _WapiHandle_thread *thread_handle = NULL;
256         gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
257                                   (gpointer *)&thread_handle);
258                                   
259         if (ok == TRUE)
260                 thread_handle->priority = priority;
261 }
262
263 /**
264  * _wapi_thread_posix_priority_to_priority:
265  *
266  *   Convert a POSIX priority to a WapiThreadPriority.
267  * sched_priority is a POSIX priority,
268  * policy is the current scheduling policy
269  */
270 static WapiThreadPriority 
271 _wapi_thread_posix_priority_to_priority (int sched_priority, int policy)
272 {
273 /* Necessary to get valid priority range */
274 #ifdef _POSIX_PRIORITY_SCHEDULING
275         int max,
276             min,
277             i,
278             priority,
279             chunk;
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
288         };
289             
290         max = sched_get_priority_max (policy);
291         min = sched_get_priority_min (policy);
292         
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;
298                      ++i) {
299                         priority += chunk;
300                 }
301                 
302                 if (max <= priority)
303                 {
304                         return (THREAD_PRIORITY_HIGHEST);
305                 }
306                 else
307                 {
308                         return (priorities[i-1]);
309                 }
310         }
311 #endif
312
313         return (THREAD_PRIORITY_NORMAL);
314 }
315
316 /**
317  * wapi_thread_priority_to_posix_priority:
318  *
319  *   Convert a WapiThreadPriority to a POSIX priority.
320  * priority is a WapiThreadPriority,
321  * policy is the current scheduling policy
322  */
323 int 
324 wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
325 {
326 /* Necessary to get valid priority range */
327 #ifdef _POSIX_PRIORITY_SCHEDULING
328         int max,
329             min,
330             posix_priority,
331             i;
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
340         };
341         
342         max = sched_get_priority_max (policy);
343         min = sched_get_priority_min (policy);
344
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)
352                                 {
353                                         return max;
354                                 }
355                                 else {
356                                         return posix_priority;
357                                 }
358                         }
359                 }
360         }
361 #endif
362
363         switch (policy) {
364                 case SCHED_FIFO:
365                 case SCHED_RR:
366                         return 50;
367 #ifdef SCHED_BATCH
368                 case SCHED_BATCH:
369 #endif
370                 case SCHED_OTHER:
371                         return 0;
372                 default:
373                         return -1;
374         }
375 }
376
377 /**
378  * GetThreadPriority:
379  * @param handle: The thread handle to query.
380  *
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.
384  */
385 gint32 
386 GetThreadPriority (gpointer handle)
387 {
388         struct _WapiHandle_thread *thread_handle = NULL;
389         int policy;
390         struct sched_param param;
391         gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
392                                   (gpointer *)&thread_handle);
393                                   
394         if (ok == FALSE)
395                 return (THREAD_PRIORITY_NORMAL);
396         
397         switch (pthread_getschedparam (thread_handle->id, &policy, &param)) {
398                 case 0:
399                         if ((policy == SCHED_FIFO) || (policy == SCHED_RR))
400                                 return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
401                         else
402                                 return (thread_handle->priority);
403                 case ESRCH:
404                         g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
405         }
406         
407         return (THREAD_PRIORITY_NORMAL);
408 }
409
410 /**
411  * SetThreadPriority:
412  * @param handle: The thread handle to query.
413  * @param priority: The priority to give to the thread.
414  *
415  * Sets the priority of the given thread.
416  * @return: TRUE on success, FALSE on failure or error.
417  */
418 gboolean 
419 SetThreadPriority (gpointer handle, gint32 priority)
420 {
421         struct _WapiHandle_thread *thread_handle = NULL;
422         int policy,
423             posix_priority,
424             rv;
425         struct sched_param param;
426         gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
427                                   (gpointer *)&thread_handle);
428                                   
429         if (ok == FALSE) {
430                 return ok;
431         }
432         
433         rv = pthread_getschedparam (thread_handle->id, &policy, &param);
434         if (rv) {
435                 if (ESRCH == rv)
436                         g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
437                 return FALSE;
438         }
439         
440         posix_priority =  wapi_thread_priority_to_posix_priority (priority, policy);
441         if (0 > posix_priority)
442                 return FALSE;
443                 
444         param.sched_priority = posix_priority;
445         switch (pthread_setschedparam (thread_handle->id, policy, &param)) {
446                 case 0:
447                         thread_handle->priority = priority;
448                         return TRUE;
449                 case ESRCH:
450                         g_warning ("pthread_setschedprio: error looking up thread id %x", (gsize)thread_handle->id);
451                         break;
452                 case ENOTSUP:
453                         g_warning ("%s: priority %d not supported", __func__, priority);
454                         break;
455                 case EPERM:
456                         g_warning ("%s: permission denied", __func__);
457                         break;
458         }
459         
460         return FALSE;
461 }
462
463 char*
464 wapi_current_thread_desc (void)
465 {
466         WapiHandle_thread *thread;
467         gpointer thread_handle;
468         int i;
469         GString* text;
470         char *res;
471
472         thread_handle = get_current_thread_handle ();
473         thread = lookup_thread (thread_handle);
474
475         text = g_string_new (0);
476         g_string_append_printf (text, "thread handle %p state : ", thread_handle);
477
478         mono_thread_info_describe_interrupt_token (mono_thread_info_current (), text);
479
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, ")");
484
485         res = text->str;
486         g_string_free (text, FALSE);
487         return res;
488 }