First set of licensing changes
[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 struct _WapiHandleOps _wapi_thread_ops = {
42         NULL,                           /* close */
43         NULL,                           /* signal */
44         NULL,                           /* own */
45         NULL,                           /* is_owned */
46         NULL,                           /* special_wait */
47         NULL                            /* prewait */
48 };
49  
50 typedef enum {
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
56 } WapiThreadPriority;
57
58 static mono_once_t thread_ops_once = MONO_ONCE_INIT;
59
60 static void
61 thread_ops_init (void)
62 {
63         _wapi_handle_register_capabilities (WAPI_HANDLE_THREAD,
64                                             WAPI_HANDLE_CAP_WAIT);
65 }
66
67 void
68 _wapi_thread_cleanup (void)
69 {
70 }
71
72 static gpointer
73 get_current_thread_handle (void)
74 {
75         MonoThreadInfo *info;
76
77         info = mono_thread_info_current ();
78         g_assert (info);
79         g_assert (info->handle);
80         return info->handle;
81 }
82
83 static WapiHandle_thread*
84 lookup_thread (HANDLE handle)
85 {
86         WapiHandle_thread *thread;
87         gboolean ok;
88
89         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
90                                                           (gpointer *)&thread);
91         g_assert (ok);
92         return thread;
93 }
94
95 static WapiHandle_thread*
96 get_current_thread (void)
97 {
98         gpointer handle;
99
100         handle = get_current_thread_handle ();
101         return lookup_thread (handle);
102 }
103
104 void
105 wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
106 {
107         WapiHandle_thread *thread_handle;
108         int i, thr_ret;
109         pid_t pid = _wapi_getpid ();
110         pthread_t tid = pthread_self ();
111         
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
116                  */
117                 return;
118         }
119
120         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p terminating", __func__, handle);
121
122         thread_handle = lookup_thread (handle);
123
124         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p abandoning held mutexes", __func__, handle);
125
126         for (i = 0; i < thread_handle->owned_mutexes->len; i++) {
127                 gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i);
128
129                 _wapi_mutex_abandon (mutex, pid, tid);
130                 _wapi_thread_disown_mutex (mutex);
131         }
132         g_ptr_array_free (thread_handle->owned_mutexes, TRUE);
133         
134         thr_ret = _wapi_handle_lock_handle (handle);
135         g_assert (thr_ret == 0);
136
137         _wapi_handle_set_signal_state (handle, TRUE, TRUE);
138
139         thr_ret = _wapi_handle_unlock_handle (handle);
140         g_assert (thr_ret == 0);
141         
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);
144         
145         /* The thread is no longer active, so unref it */
146         _wapi_handle_unref (handle);
147 }
148
149 /*
150  * wapi_create_thread_handle:
151  *
152  *   Create a thread handle for the current thread.
153  */
154 gpointer
155 wapi_create_thread_handle (void)
156 {
157         WapiHandle_thread thread_handle = {0}, *thread;
158         gpointer handle;
159
160         mono_once (&thread_ops_once, thread_ops_init);
161
162         thread_handle.owned_mutexes = g_ptr_array_new ();
163
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);
168                 
169                 return NULL;
170         }
171
172         thread = lookup_thread (handle);
173
174         thread->id = pthread_self ();
175
176         /*
177          * Hold a reference while the thread is active, because we use
178          * the handle to store thread exit information
179          */
180         _wapi_handle_ref (handle);
181
182         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: started thread id %ld", __func__, thread->id);
183         
184         return handle;
185 }
186
187 void
188 wapi_ref_thread_handle (gpointer handle)
189 {
190         _wapi_handle_ref (handle);
191 }
192
193 gpointer
194 wapi_get_current_thread_handle (void)
195 {
196         return get_current_thread_handle ();
197 }
198
199 gboolean
200 _wapi_thread_cur_apc_pending (void)
201 {
202         return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
203 }
204
205 void
206 _wapi_thread_own_mutex (gpointer mutex)
207 {
208         WapiHandle_thread *thread;
209         
210         thread = get_current_thread ();
211
212         _wapi_handle_ref (mutex);
213         
214         g_ptr_array_add (thread->owned_mutexes, mutex);
215 }
216
217 void
218 _wapi_thread_disown_mutex (gpointer mutex)
219 {
220         WapiHandle_thread *thread;
221
222         thread = get_current_thread ();
223
224         _wapi_handle_unref (mutex);
225         
226         g_ptr_array_remove (thread->owned_mutexes, mutex);
227 }
228
229 /**
230  * _wapi_thread_posix_priority_to_priority:
231  *
232  *   Convert a POSIX priority to a WapiThreadPriority.
233  * sched_priority is a POSIX priority,
234  * policy is the current scheduling policy
235  */
236 static WapiThreadPriority 
237 _wapi_thread_posix_priority_to_priority (int sched_priority, int policy)
238 {
239 /* Necessary to get valid priority range */
240 #ifdef _POSIX_PRIORITY_SCHEDULING
241         int max,
242             min,
243             i,
244             priority,
245             chunk;
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
254         };
255             
256         max = sched_get_priority_max (policy);
257         min = sched_get_priority_min (policy);
258         
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;
264                      ++i) {
265                         priority += chunk;
266                 }
267                 
268                 if (max <= priority)
269                 {
270                         return (THREAD_PRIORITY_HIGHEST);
271                 }
272                 else
273                 {
274                         return (priorities[i-1]);
275                 }
276         }
277 #endif
278
279         return (THREAD_PRIORITY_NORMAL);
280 }
281
282 /**
283  * _wapi_thread_priority_to_posix_priority:
284  *
285  *   Convert a WapiThreadPriority to a POSIX priority.
286  * priority is a WapiThreadPriority,
287  * policy is the current scheduling policy
288  */
289 static int 
290 _wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
291 {
292 /* Necessary to get valid priority range */
293 #ifdef _POSIX_PRIORITY_SCHEDULING
294         int max,
295             min,
296             posix_priority,
297             i;
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
306         };
307         
308         max = sched_get_priority_max (policy);
309         min = sched_get_priority_min (policy);
310
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)
318                                 {
319                                         return max;
320                                 }
321                                 else {
322                                         return posix_priority;
323                                 }
324                         }
325                 }
326         }
327 #endif
328
329         switch (policy) {
330                 case SCHED_FIFO:
331                 case SCHED_RR:
332                         return 50;
333 #ifdef SCHED_BATCH
334                 case SCHED_BATCH:
335 #endif
336                 case SCHED_OTHER:
337                         return 0;
338                 default:
339                         return -1;
340         }
341 }
342
343 /**
344  * GetThreadPriority:
345  * @param handle: The thread handle to query.
346  *
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.
350  */
351 gint32 
352 GetThreadPriority (gpointer handle)
353 {
354         struct _WapiHandle_thread *thread_handle;
355         int policy;
356         struct sched_param param;
357         gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
358                                   (gpointer *)&thread_handle);
359                                   
360         if (ok == FALSE) {
361                 return (THREAD_PRIORITY_NORMAL);
362         }
363         
364         switch (pthread_getschedparam (thread_handle->id, &policy, &param)) {
365                 case 0:
366                         return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
367                 case ESRCH:
368                         g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
369         }
370         
371         return (THREAD_PRIORITY_NORMAL);
372 }
373
374 /**
375  * SetThreadPriority:
376  * @param handle: The thread handle to query.
377  * @param priority: The priority to give to the thread.
378  *
379  * Sets the priority of the given thread.
380  * @return: TRUE on success, FALSE on failure or error.
381  */
382 gboolean 
383 SetThreadPriority (gpointer handle, gint32 priority)
384 {
385         struct _WapiHandle_thread *thread_handle;
386         int policy,
387             posix_priority,
388             rv;
389         struct sched_param param;
390         gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
391                                   (gpointer *)&thread_handle);
392                                   
393         if (ok == FALSE) {
394                 return ok;
395         }
396         
397         rv = pthread_getschedparam (thread_handle->id, &policy, &param);
398         if (rv) {
399                 if (ESRCH == rv)
400                         g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
401                 return FALSE;
402         }
403         
404         posix_priority =  _wapi_thread_priority_to_posix_priority (priority, policy);
405         if (0 > posix_priority)
406                 return FALSE;
407                 
408         param.sched_priority = posix_priority;
409         switch (pthread_setschedparam (thread_handle->id, policy, &param)) {
410                 case 0:
411                         return TRUE;
412                 case ESRCH:
413                         g_warning ("pthread_setschedparam: error looking up thread id %x", (gsize)thread_handle->id);
414                         break;
415                 case ENOTSUP:
416                         g_warning ("%s: priority %d not supported", __func__, priority);
417                         break;
418                 case EPERM:
419                         g_warning ("%s: permission denied", __func__);
420                         break;
421         }
422         
423         return FALSE;
424 }
425
426 char*
427 wapi_current_thread_desc (void)
428 {
429         WapiHandle_thread *thread;
430         gpointer thread_handle;
431         int i;
432         GString* text;
433         char *res;
434
435         thread_handle = get_current_thread_handle ();
436         thread = lookup_thread (thread_handle);
437
438         text = g_string_new (0);
439         g_string_append_printf (text, "thread handle %p state : ", thread_handle);
440
441         mono_thread_info_describe_interrupt_token (mono_thread_info_current (), text);
442
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, ")");
447
448         res = text->str;
449         g_string_free (text, FALSE);
450         return res;
451 }