bb65b5f799cd44f8279b4419777e5a16210f6aa9
[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  */
11
12 #include <config.h>
13 #include <stdio.h>
14 #include <glib.h>
15 #include <string.h>
16 #include <pthread.h>
17 #include <sched.h>
18 #include <sys/time.h>
19 #include <errno.h>
20 #include <sys/types.h>
21 #include <unistd.h>
22
23 #include <mono/io-layer/wapi.h>
24 #include <mono/io-layer/wapi-private.h>
25 #include <mono/io-layer/handles-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>
29
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
36 #ifdef HAVE_VALGRIND_MEMCHECK_H
37 #include <valgrind/memcheck.h>
38 #endif
39
40 struct _WapiHandleOps _wapi_thread_ops = {
41         NULL,                           /* close */
42         NULL,                           /* signal */
43         NULL,                           /* own */
44         NULL,                           /* is_owned */
45         NULL,                           /* special_wait */
46         NULL                            /* prewait */
47 };
48  
49 typedef enum {
50         THREAD_PRIORITY_LOWEST = -2,
51         THREAD_PRIORITY_BELOW_NORMAL = -1,
52         THREAD_PRIORITY_NORMAL = 0,
53         THREAD_PRIORITY_ABOVE_NORMAL = 1,
54         THREAD_PRIORITY_HIGHEST = 2
55 } WapiThreadPriority;
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 void
67 _wapi_thread_cleanup (void)
68 {
69 }
70
71 static gpointer
72 get_current_thread_handle (void)
73 {
74         MonoThreadInfo *info;
75
76         info = mono_thread_info_current ();
77         g_assert (info);
78         g_assert (info->handle);
79         return info->handle;
80 }
81
82 static WapiHandle_thread*
83 lookup_thread (HANDLE handle)
84 {
85         WapiHandle_thread *thread;
86         gboolean ok;
87
88         ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
89                                                           (gpointer *)&thread);
90         g_assert (ok);
91         return thread;
92 }
93
94 static WapiHandle_thread*
95 get_current_thread (void)
96 {
97         gpointer handle;
98
99         handle = get_current_thread_handle ();
100         return lookup_thread (handle);
101 }
102
103 void
104 wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
105 {
106         WapiHandle_thread *thread_handle;
107         int i, thr_ret;
108         pid_t pid = _wapi_getpid ();
109         pthread_t tid = pthread_self ();
110         
111         if (_wapi_handle_issignalled (handle) ||
112             _wapi_handle_type (handle) == WAPI_HANDLE_UNUSED) {
113                 /* We must have already deliberately finished with
114                  * this thread, so don't do any more now
115                  */
116                 return;
117         }
118
119         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p terminating", __func__, handle);
120
121         thread_handle = lookup_thread (handle);
122
123         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p abandoning held mutexes", __func__, handle);
124
125         for (i = 0; i < thread_handle->owned_mutexes->len; i++) {
126                 gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i);
127
128                 _wapi_mutex_abandon (mutex, pid, tid);
129                 _wapi_thread_disown_mutex (mutex);
130         }
131         g_ptr_array_free (thread_handle->owned_mutexes, TRUE);
132         
133         thr_ret = _wapi_handle_lock_handle (handle);
134         g_assert (thr_ret == 0);
135
136         _wapi_handle_set_signal_state (handle, TRUE, TRUE);
137
138         thr_ret = _wapi_handle_unlock_handle (handle);
139         g_assert (thr_ret == 0);
140         
141         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Recording thread handle %p id %ld status as %d",
142                   __func__, handle, thread_handle->id, exitstatus);
143         
144         /* The thread is no longer active, so unref it */
145         _wapi_handle_unref (handle);
146 }
147
148 /*
149  * wapi_create_thread_handle:
150  *
151  *   Create a thread handle for the current thread.
152  */
153 gpointer
154 wapi_create_thread_handle (void)
155 {
156         WapiHandle_thread thread_handle = {0}, *thread;
157         gpointer handle;
158
159         mono_once (&thread_ops_once, thread_ops_init);
160
161         thread_handle.owned_mutexes = g_ptr_array_new ();
162
163         handle = _wapi_handle_new (WAPI_HANDLE_THREAD, &thread_handle);
164         if (handle == _WAPI_HANDLE_INVALID) {
165                 g_warning ("%s: error creating thread handle", __func__);
166                 SetLastError (ERROR_GEN_FAILURE);
167                 
168                 return NULL;
169         }
170
171         thread = lookup_thread (handle);
172
173         thread->id = pthread_self ();
174
175         /*
176          * Hold a reference while the thread is active, because we use
177          * the handle to store thread exit information
178          */
179         _wapi_handle_ref (handle);
180
181         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: started thread id %ld", __func__, thread->id);
182         
183         return handle;
184 }
185
186 void
187 wapi_ref_thread_handle (gpointer handle)
188 {
189         _wapi_handle_ref (handle);
190 }
191
192 gpointer
193 wapi_get_current_thread_handle (void)
194 {
195         return get_current_thread_handle ();
196 }
197
198 gboolean
199 _wapi_thread_cur_apc_pending (void)
200 {
201         return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
202 }
203
204 void
205 _wapi_thread_own_mutex (gpointer mutex)
206 {
207         WapiHandle_thread *thread;
208         
209         thread = get_current_thread ();
210
211         _wapi_handle_ref (mutex);
212         
213         g_ptr_array_add (thread->owned_mutexes, mutex);
214 }
215
216 void
217 _wapi_thread_disown_mutex (gpointer mutex)
218 {
219         WapiHandle_thread *thread;
220
221         thread = get_current_thread ();
222
223         _wapi_handle_unref (mutex);
224         
225         g_ptr_array_remove (thread->owned_mutexes, mutex);
226 }
227
228 /**
229  * _wapi_thread_posix_priority_to_priority:
230  *
231  *   Convert a POSIX priority to a WapiThreadPriority.
232  * sched_priority is a POSIX priority,
233  * policy is the current scheduling policy
234  */
235 static WapiThreadPriority 
236 _wapi_thread_posix_priority_to_priority (int sched_priority, int policy)
237 {
238 /* Necessary to get valid priority range */
239 #ifdef _POSIX_PRIORITY_SCHEDULING
240         int max,
241             min,
242             i,
243             priority,
244             chunk;
245         WapiThreadPriority priorities[] = {
246                 THREAD_PRIORITY_LOWEST,
247                 THREAD_PRIORITY_LOWEST,
248                 THREAD_PRIORITY_BELOW_NORMAL,
249                 THREAD_PRIORITY_NORMAL,
250                 THREAD_PRIORITY_ABOVE_NORMAL,
251                 THREAD_PRIORITY_HIGHEST,
252                 THREAD_PRIORITY_HIGHEST
253         };
254             
255         max = sched_get_priority_max (policy);
256         min = sched_get_priority_min (policy);
257         
258         /* Partition priority range linearly, 
259            assign each partition a thread priority */
260         if (max != min && 0 <= max && 0 <= min) {
261                 for (i=1, priority=min, chunk=(max-min)/7; 
262                      i<6 && sched_priority > priority;
263                      ++i) {
264                         priority += chunk;
265                 }
266                 
267                 if (max <= priority)
268                 {
269                         return (THREAD_PRIORITY_HIGHEST);
270                 }
271                 else
272                 {
273                         return (priorities[i-1]);
274                 }
275         }
276 #endif
277
278         return (THREAD_PRIORITY_NORMAL);
279 }
280
281 /**
282  * _wapi_thread_priority_to_posix_priority:
283  *
284  *   Convert a WapiThreadPriority to a POSIX priority.
285  * priority is a WapiThreadPriority,
286  * policy is the current scheduling policy
287  */
288 static int 
289 _wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
290 {
291 /* Necessary to get valid priority range */
292 #ifdef _POSIX_PRIORITY_SCHEDULING
293         int max,
294             min,
295             posix_priority,
296             i;
297         WapiThreadPriority priorities[] = {
298                 THREAD_PRIORITY_LOWEST,
299                 THREAD_PRIORITY_LOWEST,
300                 THREAD_PRIORITY_BELOW_NORMAL,
301                 THREAD_PRIORITY_NORMAL,
302                 THREAD_PRIORITY_ABOVE_NORMAL,
303                 THREAD_PRIORITY_HIGHEST,
304                 THREAD_PRIORITY_HIGHEST
305         };
306         
307         max = sched_get_priority_max (policy);
308         min = sched_get_priority_min (policy);
309
310         /* Partition priority range linearly, 
311            numerically approximate matching ThreadPriority */
312         if (max != min && 0 <= max && 0 <= min) {
313                 for (i=0; i<7; ++i) {
314                         if (priorities[i] == priority) {
315                                 posix_priority = min + ((max-min)/7) * i;
316                                 if (max < posix_priority)
317                                 {
318                                         return max;
319                                 }
320                                 else {
321                                         return posix_priority;
322                                 }
323                         }
324                 }
325         }
326 #endif
327
328         switch (policy) {
329                 case SCHED_FIFO:
330                 case SCHED_RR:
331                         return 50;
332 #ifdef SCHED_BATCH
333                 case SCHED_BATCH:
334 #endif
335                 case SCHED_OTHER:
336                         return 0;
337                 default:
338                         return -1;
339         }
340 }
341
342 /**
343  * GetThreadPriority:
344  * @param handle: The thread handle to query.
345  *
346  * Gets the priority of the given thread.
347  * @return: A MonoThreadPriority approximating the current POSIX 
348  * thread priority, or THREAD_PRIORITY_NORMAL on error.
349  */
350 gint32 
351 GetThreadPriority (gpointer handle)
352 {
353         struct _WapiHandle_thread *thread_handle;
354         int policy;
355         struct sched_param param;
356         gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
357                                   (gpointer *)&thread_handle);
358                                   
359         if (ok == FALSE) {
360                 return (THREAD_PRIORITY_NORMAL);
361         }
362         
363         switch (pthread_getschedparam (thread_handle->id, &policy, &param)) {
364                 case 0:
365                         return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
366                 case ESRCH:
367                         g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
368         }
369         
370         return (THREAD_PRIORITY_NORMAL);
371 }
372
373 /**
374  * SetThreadPriority:
375  * @param handle: The thread handle to query.
376  * @param priority: The priority to give to the thread.
377  *
378  * Sets the priority of the given thread.
379  * @return: TRUE on success, FALSE on failure or error.
380  */
381 gboolean 
382 SetThreadPriority (gpointer handle, gint32 priority)
383 {
384         struct _WapiHandle_thread *thread_handle;
385         int policy,
386             posix_priority,
387             rv;
388         struct sched_param param;
389         gboolean ok = _wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
390                                   (gpointer *)&thread_handle);
391                                   
392         if (ok == FALSE) {
393                 return ok;
394         }
395         
396         rv = pthread_getschedparam (thread_handle->id, &policy, &param);
397         if (rv) {
398                 if (ESRCH == rv)
399                         g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
400                 return FALSE;
401         }
402         
403         posix_priority =  _wapi_thread_priority_to_posix_priority (priority, policy);
404         if (0 > posix_priority)
405                 return FALSE;
406                 
407         param.sched_priority = posix_priority;
408         switch (pthread_setschedparam (thread_handle->id, policy, &param)) {
409                 case 0:
410                         return TRUE;
411                 case ESRCH:
412                         g_warning ("pthread_setschedparam: error looking up thread id %x", (gsize)thread_handle->id);
413                         break;
414                 case ENOTSUP:
415                         g_warning ("%s: priority %d not supported", __func__, priority);
416                         break;
417                 case EPERM:
418                         g_warning ("%s: permission denied", __func__);
419                         break;
420         }
421         
422         return FALSE;
423 }
424
425 char*
426 wapi_current_thread_desc (void)
427 {
428         WapiHandle_thread *thread;
429         gpointer thread_handle;
430         int i;
431         GString* text;
432         char *res;
433
434         thread_handle = get_current_thread_handle ();
435         thread = lookup_thread (thread_handle);
436
437         text = g_string_new (0);
438         g_string_append_printf (text, "thread handle %p state : ", thread_handle);
439
440         mono_thread_info_describe_interrupt_token (mono_thread_info_current (), text);
441
442         g_string_append_printf (text, " owns (");
443         for (i = 0; i < thread->owned_mutexes->len; i++)
444                 g_string_append_printf (text, i > 0 ? ", %p" : "%p", g_ptr_array_index (thread->owned_mutexes, i));
445         g_string_append_printf (text, ")");
446
447         res = text->str;
448         g_string_free (text, FALSE);
449         return res;
450 }