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