Merge pull request #3262 from lindenlab/add_continuations_test
[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/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 #include <mono/utils/w32handle.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 static MonoW32HandleOps _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 void
58 _wapi_thread_init (void)
59 {
60         mono_w32handle_register_ops (MONO_W32HANDLE_THREAD, &_wapi_thread_ops);
61
62         mono_w32handle_register_capabilities (MONO_W32HANDLE_THREAD, MONO_W32HANDLE_CAP_WAIT);
63 }
64
65 static void thread_details (gpointer data)
66 {
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);
70 }
71
72 static const gchar* thread_typename (void)
73 {
74         return "Thread";
75 }
76
77 static gsize thread_typesize (void)
78 {
79         return sizeof (WapiHandle_thread);
80 }
81
82 void
83 _wapi_thread_cleanup (void)
84 {
85 }
86
87 static gpointer
88 get_current_thread_handle (void)
89 {
90         MonoThreadInfo *info;
91
92         info = mono_thread_info_current ();
93         g_assert (info);
94         g_assert (info->handle);
95         return info->handle;
96 }
97
98 static WapiHandle_thread*
99 lookup_thread (HANDLE handle)
100 {
101         WapiHandle_thread *thread;
102         gboolean ok;
103
104         ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_THREAD,
105                                                           (gpointer *)&thread);
106         g_assert (ok);
107         return thread;
108 }
109
110 static WapiHandle_thread*
111 get_current_thread (void)
112 {
113         gpointer handle;
114
115         handle = get_current_thread_handle ();
116         return lookup_thread (handle);
117 }
118
119 void
120 wapi_thread_handle_set_exited (gpointer handle, guint32 exitstatus)
121 {
122         WapiHandle_thread *thread_handle;
123         int i, thr_ret;
124         pid_t pid = _wapi_getpid ();
125         pthread_t tid = pthread_self ();
126         
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
131                  */
132                 return;
133         }
134
135         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p terminating", __func__, handle);
136
137         thread_handle = lookup_thread (handle);
138
139         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: Thread %p abandoning held mutexes", __func__, handle);
140
141         for (i = 0; i < thread_handle->owned_mutexes->len; i++) {
142                 gpointer mutex = g_ptr_array_index (thread_handle->owned_mutexes, i);
143
144                 _wapi_mutex_abandon (mutex, pid, tid);
145                 _wapi_thread_disown_mutex (mutex);
146         }
147         g_ptr_array_free (thread_handle->owned_mutexes, TRUE);
148         
149         thr_ret = mono_w32handle_lock_handle (handle);
150         g_assert (thr_ret == 0);
151
152         mono_w32handle_set_signal_state (handle, TRUE, TRUE);
153
154         thr_ret = mono_w32handle_unlock_handle (handle);
155         g_assert (thr_ret == 0);
156         
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);
159         
160         /* The thread is no longer active, so unref it */
161         mono_w32handle_unref (handle);
162 }
163
164 /*
165  * wapi_create_thread_handle:
166  *
167  *   Create a thread handle for the current thread.
168  */
169 gpointer
170 wapi_create_thread_handle (void)
171 {
172         WapiHandle_thread thread_handle = {0}, *thread;
173         gpointer handle;
174
175         thread_handle.owned_mutexes = g_ptr_array_new ();
176
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);
181                 
182                 return NULL;
183         }
184
185         thread = lookup_thread (handle);
186
187         thread->id = pthread_self ();
188
189         /*
190          * Hold a reference while the thread is active, because we use
191          * the handle to store thread exit information
192          */
193         mono_w32handle_ref (handle);
194
195         MONO_TRACE (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER, "%s: started thread id %ld", __func__, thread->id);
196         
197         return handle;
198 }
199
200 void
201 wapi_ref_thread_handle (gpointer handle)
202 {
203         mono_w32handle_ref (handle);
204 }
205
206 gpointer
207 wapi_get_current_thread_handle (void)
208 {
209         return get_current_thread_handle ();
210 }
211
212 gboolean
213 _wapi_thread_cur_apc_pending (void)
214 {
215         return mono_thread_info_is_interrupt_state (mono_thread_info_current ());
216 }
217
218 void
219 _wapi_thread_own_mutex (gpointer mutex)
220 {
221         WapiHandle_thread *thread;
222         
223         thread = get_current_thread ();
224
225         mono_w32handle_ref (mutex);
226         
227         g_ptr_array_add (thread->owned_mutexes, mutex);
228 }
229
230 void
231 _wapi_thread_disown_mutex (gpointer mutex)
232 {
233         WapiHandle_thread *thread;
234
235         thread = get_current_thread ();
236
237         mono_w32handle_unref (mutex);
238         
239         g_ptr_array_remove (thread->owned_mutexes, mutex);
240 }
241
242 /**
243  * wapi_init_thread_info_priority:
244  * @param handle: The thread handle to set.
245  * @param priority: Priority to initialize with
246  *
247  *   Initialize the priority field of the thread info
248  */
249 void
250 wapi_init_thread_info_priority (gpointer handle, gint32 priority)
251 {
252         struct _WapiHandle_thread *thread_handle = NULL;
253         gboolean ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_THREAD,
254                                   (gpointer *)&thread_handle);
255                                   
256         if (ok == TRUE)
257                 thread_handle->priority = priority;
258 }
259
260 /**
261  * _wapi_thread_posix_priority_to_priority:
262  *
263  *   Convert a POSIX priority to a WapiThreadPriority.
264  * sched_priority is a POSIX priority,
265  * policy is the current scheduling policy
266  */
267 static WapiThreadPriority 
268 _wapi_thread_posix_priority_to_priority (int sched_priority, int policy)
269 {
270 /* Necessary to get valid priority range */
271 #ifdef _POSIX_PRIORITY_SCHEDULING
272         int max,
273             min,
274             i,
275             priority,
276             chunk;
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
285         };
286             
287         max = sched_get_priority_max (policy);
288         min = sched_get_priority_min (policy);
289         
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;
295                      ++i) {
296                         priority += chunk;
297                 }
298                 
299                 if (max <= priority)
300                 {
301                         return (THREAD_PRIORITY_HIGHEST);
302                 }
303                 else
304                 {
305                         return (priorities[i-1]);
306                 }
307         }
308 #endif
309
310         return (THREAD_PRIORITY_NORMAL);
311 }
312
313 /**
314  * wapi_thread_priority_to_posix_priority:
315  *
316  *   Convert a WapiThreadPriority to a POSIX priority.
317  * priority is a WapiThreadPriority,
318  * policy is the current scheduling policy
319  */
320 int 
321 wapi_thread_priority_to_posix_priority (WapiThreadPriority priority, int policy)
322 {
323 /* Necessary to get valid priority range */
324 #ifdef _POSIX_PRIORITY_SCHEDULING
325         int max,
326             min,
327             posix_priority,
328             i;
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
337         };
338         
339         max = sched_get_priority_max (policy);
340         min = sched_get_priority_min (policy);
341
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)
349                                 {
350                                         return max;
351                                 }
352                                 else {
353                                         return posix_priority;
354                                 }
355                         }
356                 }
357         }
358 #endif
359
360         switch (policy) {
361                 case SCHED_FIFO:
362                 case SCHED_RR:
363                         return 50;
364 #ifdef SCHED_BATCH
365                 case SCHED_BATCH:
366 #endif
367                 case SCHED_OTHER:
368                         return 0;
369                 default:
370                         return -1;
371         }
372 }
373
374 /**
375  * GetThreadPriority:
376  * @param handle: The thread handle to query.
377  *
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.
381  */
382 gint32 
383 GetThreadPriority (gpointer handle)
384 {
385         struct _WapiHandle_thread *thread_handle = NULL;
386         int policy;
387         struct sched_param param;
388         gboolean ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_THREAD,
389                                   (gpointer *)&thread_handle);
390                                   
391         if (ok == FALSE)
392                 return (THREAD_PRIORITY_NORMAL);
393         
394         switch (pthread_getschedparam (thread_handle->id, &policy, &param)) {
395                 case 0:
396                         if ((policy == SCHED_FIFO) || (policy == SCHED_RR))
397                                 return (_wapi_thread_posix_priority_to_priority (param.sched_priority, policy));
398                         else
399                                 return (thread_handle->priority);
400                 case ESRCH:
401                         g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
402         }
403         
404         return (THREAD_PRIORITY_NORMAL);
405 }
406
407 /**
408  * SetThreadPriority:
409  * @param handle: The thread handle to query.
410  * @param priority: The priority to give to the thread.
411  *
412  * Sets the priority of the given thread.
413  * @return: TRUE on success, FALSE on failure or error.
414  */
415 gboolean 
416 SetThreadPriority (gpointer handle, gint32 priority)
417 {
418         struct _WapiHandle_thread *thread_handle = NULL;
419         int policy,
420             posix_priority,
421             rv;
422         struct sched_param param;
423         gboolean ok = mono_w32handle_lookup (handle, MONO_W32HANDLE_THREAD,
424                                   (gpointer *)&thread_handle);
425                                   
426         if (ok == FALSE) {
427                 return ok;
428         }
429         
430         rv = pthread_getschedparam (thread_handle->id, &policy, &param);
431         if (rv) {
432                 if (ESRCH == rv)
433                         g_warning ("pthread_getschedparam: error looking up thread id %x", (gsize)thread_handle->id);
434                 return FALSE;
435         }
436         
437         posix_priority =  wapi_thread_priority_to_posix_priority (priority, policy);
438         if (0 > posix_priority)
439                 return FALSE;
440                 
441         param.sched_priority = posix_priority;
442         switch (pthread_setschedparam (thread_handle->id, policy, &param)) {
443                 case 0:
444                         thread_handle->priority = priority;
445                         return TRUE;
446                 case ESRCH:
447                         g_warning ("pthread_setschedprio: error looking up thread id %x", (gsize)thread_handle->id);
448                         break;
449                 case ENOTSUP:
450                         g_warning ("%s: priority %d not supported", __func__, priority);
451                         break;
452                 case EPERM:
453                         g_warning ("%s: permission denied", __func__);
454                         break;
455         }
456         
457         return FALSE;
458 }
459
460 char*
461 wapi_current_thread_desc (void)
462 {
463         WapiHandle_thread *thread;
464         gpointer thread_handle;
465         int i;
466         GString* text;
467         char *res;
468
469         thread_handle = get_current_thread_handle ();
470         thread = lookup_thread (thread_handle);
471
472         text = g_string_new (0);
473         g_string_append_printf (text, "thread handle %p state : ", thread_handle);
474
475         mono_thread_info_describe_interrupt_token (mono_thread_info_current (), text);
476
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, ")");
481
482         res = text->str;
483         g_string_free (text, FALSE);
484         return res;
485 }