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