2002-08-01 Dick Porter <dick@ximian.com>
[mono.git] / mono / io-layer / threads.c
1 /*
2  * threads.c:  Thread handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #if HAVE_BOEHM_GC
12 #include <gc/gc.h>
13 #include "mono/utils/mono-hash.h"
14 #endif
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
22 #include <mono/io-layer/wapi.h>
23 #include <mono/io-layer/wapi-private.h>
24 #include <mono/io-layer/timed-thread.h>
25 #include <mono/io-layer/handles-private.h>
26 #include <mono/io-layer/misc-private.h>
27 #include <mono/io-layer/mono-mutex.h>
28 #include <mono/io-layer/thread-private.h>
29 #include <mono/io-layer/mono-spinlock.h>
30
31 #undef DEBUG
32 #undef TLS_DEBUG
33 #undef TLS_PTHREAD_MUTEX
34
35 /* Hash threads with tids. I thought of using TLS for this, but that
36  * would have to set the data in the new thread, which is more hassle
37  */
38 static mono_once_t thread_hash_once = MONO_ONCE_INIT;
39 static mono_mutex_t thread_hash_mutex = MONO_MUTEX_INITIALIZER;
40 static GHashTable *thread_hash=NULL;
41
42 #if HAVE_BOEHM_GC
43 static MonoGHashTable *tls_gc_hash = NULL;
44 #endif
45
46 static void thread_close_private (gpointer handle);
47 static void thread_own (gpointer handle);
48
49 struct _WapiHandleOps _wapi_thread_ops = {
50         NULL,                           /* close_shared */
51         thread_close_private,           /* close_private */
52         NULL,                           /* signal */
53         thread_own,                     /* own */
54         NULL,                           /* is_owned */
55 };
56
57 static mono_once_t thread_ops_once=MONO_ONCE_INIT;
58
59 static void thread_ops_init (void)
60 {
61         _wapi_handle_register_capabilities (WAPI_HANDLE_THREAD,
62                                             WAPI_HANDLE_CAP_WAIT);
63 }
64
65 static void thread_close_private (gpointer handle)
66 {
67         struct _WapiHandlePrivate_thread *thread_handle;
68         gboolean ok;
69         
70         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD, NULL,
71                                 (gpointer *)&thread_handle);
72         if(ok==FALSE) {
73                 g_warning (G_GNUC_PRETTY_FUNCTION
74                            ": error looking up thread handle %p", handle);
75                 return;
76         }
77         
78 #ifdef DEBUG
79         g_message(G_GNUC_PRETTY_FUNCTION
80                   ": closing thread handle %p with thread %p id %ld",
81                   handle, thread_handle->thread,
82                   thread_handle->thread->id);
83 #endif
84
85         if(thread_handle->thread!=NULL) {
86                 _wapi_timed_thread_destroy (thread_handle->thread);
87         }
88 }
89
90 static void thread_own (gpointer handle)
91 {
92         struct _WapiHandle_thread *thread_handle;
93         struct _WapiHandlePrivate_thread *thread_private_handle;
94         gboolean ok;
95         
96         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
97                                 (gpointer *)&thread_handle,
98                                 (gpointer *)&thread_private_handle);
99         if(ok==FALSE) {
100                 g_warning (G_GNUC_PRETTY_FUNCTION
101                            ": error looking up thread handle %p", handle);
102                 return;
103         }
104
105         if(thread_private_handle->joined==FALSE) {
106                 _wapi_timed_thread_join (thread_private_handle->thread, NULL,
107                                          NULL);
108                 thread_private_handle->joined=TRUE;
109         }
110 }
111
112 static void thread_exit(guint32 exitstatus, gpointer handle)
113 {
114         struct _WapiHandle_thread *thread_handle;
115         struct _WapiHandlePrivate_thread *thread_private_handle;
116         gboolean ok;
117         
118         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
119                                 (gpointer *)&thread_handle,
120                                 (gpointer *)&thread_private_handle);
121         if(ok==FALSE) {
122                 g_warning (G_GNUC_PRETTY_FUNCTION
123                            ": error looking up thread handle %p", handle);
124                 return;
125         }
126
127         _wapi_handle_lock_handle (handle);
128
129 #ifdef DEBUG
130         g_message (G_GNUC_PRETTY_FUNCTION
131                    ": Recording thread handle %p exit status", handle);
132 #endif
133         
134         thread_handle->exitstatus=exitstatus;
135         thread_handle->state=THREAD_STATE_EXITED;
136         _wapi_handle_set_signal_state (handle, TRUE, TRUE);
137
138         _wapi_handle_unlock_handle (handle);
139         
140 #ifdef DEBUG
141         g_message(G_GNUC_PRETTY_FUNCTION
142                   ": Recording thread handle %p id %ld status as %d",
143                   handle, thread_private_handle->thread->id, exitstatus);
144 #endif
145
146         /* Remove this thread from the hash */
147         mono_mutex_lock(&thread_hash_mutex);
148         g_hash_table_remove(thread_hash, &thread_private_handle->thread->id);
149         mono_mutex_unlock(&thread_hash_mutex);
150
151         /* The thread is no longer active, so unref it */
152         _wapi_handle_unref (handle);
153 }
154
155 static void thread_hash_init(void)
156 {
157         thread_hash=g_hash_table_new(g_int_hash, g_int_equal);
158 }
159
160 /**
161  * CreateThread:
162  * @security: Ignored for now.
163  * @stacksize: the size in bytes of the new thread's stack. Use 0 to
164  * default to the normal stack size. (Ignored for now).
165  * @start: The function that the new thread should start with
166  * @param: The parameter to give to @start.
167  * @create: If 0, the new thread is ready to run immediately.  If
168  * %CREATE_SUSPENDED, the new thread will be in the suspended state,
169  * requiring a ResumeThread() call to continue running.
170  * @tid: If non-NULL, the ID of the new thread is stored here.
171  *
172  * Creates a new threading handle.
173  *
174  * Return value: a new handle, or NULL
175  */
176 gpointer CreateThread(WapiSecurityAttributes *security G_GNUC_UNUSED, guint32 stacksize G_GNUC_UNUSED,
177                       WapiThreadStart start, gpointer param, guint32 create G_GNUC_UNUSED,
178                       guint32 *tid) 
179 {
180         struct _WapiHandle_thread *thread_handle;
181         struct _WapiHandlePrivate_thread *thread_private_handle;
182         gpointer handle;
183         gboolean ok;
184         int ret;
185         
186         mono_once(&thread_hash_once, thread_hash_init);
187         mono_once (&thread_ops_once, thread_ops_init);
188         
189         if(start==NULL) {
190                 return(NULL);
191         }
192         
193         handle=_wapi_handle_new (WAPI_HANDLE_THREAD);
194         if(handle==_WAPI_HANDLE_INVALID) {
195                 g_warning (G_GNUC_PRETTY_FUNCTION
196                            ": error creating thread handle");
197                 return(NULL);
198         }
199
200         _wapi_handle_lock_handle (handle);
201         
202         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
203                                 (gpointer *)&thread_handle,
204                                 (gpointer *)&thread_private_handle);
205         if(ok==FALSE) {
206                 g_warning (G_GNUC_PRETTY_FUNCTION
207                            ": error looking up thread handle %p", handle);
208                 _wapi_handle_unlock_handle (handle);
209                 return(NULL);
210         }
211
212         /* Hold a reference while the thread is active, because we use
213          * the handle to store thread exit information
214          */
215         _wapi_handle_ref (handle);
216
217         thread_handle->state=THREAD_STATE_START;
218         
219         /* Lock around the thread create, so that the new thread cant
220          * race us to look up the thread handle in GetCurrentThread()
221          */
222         mono_mutex_lock(&thread_hash_mutex);
223         
224         ret=_wapi_timed_thread_create(&thread_private_handle->thread, NULL,
225                                       start, thread_exit, param, handle);
226         if(ret!=0) {
227 #ifdef DEBUG
228                 g_message(G_GNUC_PRETTY_FUNCTION ": Thread create error: %s",
229                           strerror(ret));
230 #endif
231                 mono_mutex_unlock(&thread_hash_mutex);
232                 _wapi_handle_unlock_handle (handle);
233                 _wapi_handle_unref (handle);
234                 
235                 /* And again, because of the reference we took above */
236                 _wapi_handle_unref (handle);
237                 return(NULL);
238         }
239
240         g_hash_table_insert(thread_hash, &thread_private_handle->thread->id,
241                             handle);
242         mono_mutex_unlock(&thread_hash_mutex);
243         
244 #ifdef DEBUG
245         g_message(G_GNUC_PRETTY_FUNCTION
246                   ": Started thread handle %p thread %p ID %ld", handle,
247                   thread_private_handle->thread,
248                   thread_private_handle->thread->id);
249 #endif
250         
251         if(tid!=NULL) {
252                 *tid=thread_private_handle->thread->id;
253         }
254
255         _wapi_handle_unlock_handle (handle);
256         
257         return(handle);
258 }
259
260 gpointer OpenThread (guint32 access G_GNUC_UNUSED, gboolean inherit G_GNUC_UNUSED, guint32 tid)
261 {
262         gpointer ret=NULL;
263         
264 #ifdef DEBUG
265         g_message (G_GNUC_PRETTY_FUNCTION ": looking up thread %d", tid);
266 #endif
267
268         mono_mutex_lock(&thread_hash_mutex);
269
270         ret=g_hash_table_lookup(thread_hash, &tid);
271         mono_mutex_unlock(&thread_hash_mutex);
272         
273         if(ret!=NULL) {
274                 _wapi_handle_ref (ret);
275         }
276         
277 #ifdef DEBUG
278         g_message (G_GNUC_PRETTY_FUNCTION ": returning thread handle %p", ret);
279 #endif
280         
281         return(ret);
282 }
283
284 /**
285  * ExitThread:
286  * @exitcode: Sets the thread's exit code, which can be read from
287  * another thread with GetExitCodeThread().
288  *
289  * Terminates the calling thread.  A thread can also exit by returning
290  * from its start function. When the last thread in a process
291  * terminates, the process itself terminates.
292  */
293 void ExitThread(guint32 exitcode)
294 {
295         /* No thread created yet.  */
296         if (thread_hash == NULL)
297                 exit(exitcode);
298
299         _wapi_timed_thread_exit(exitcode);
300 }
301
302 /**
303  * GetExitCodeThread:
304  * @handle: The thread handle to query
305  * @exitcode: The thread @handle exit code is stored here
306  *
307  * Finds the exit code of @handle, and stores it in @exitcode.  If the
308  * thread @handle is still running, the value stored is %STILL_ACTIVE.
309  *
310  * Return value: %TRUE, or %FALSE on error.
311  */
312 gboolean GetExitCodeThread(gpointer handle, guint32 *exitcode)
313 {
314         struct _WapiHandle_thread *thread_handle;
315         struct _WapiHandlePrivate_thread *thread_private_handle;
316         gboolean ok;
317         
318         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_THREAD,
319                                 (gpointer *)&thread_handle,
320                                 (gpointer *)&thread_private_handle);
321         if(ok==FALSE) {
322                 g_warning (G_GNUC_PRETTY_FUNCTION
323                            ": error looking up thread handle %p", handle);
324                 return(FALSE);
325         }
326         
327 #ifdef DEBUG
328         g_message(G_GNUC_PRETTY_FUNCTION
329                   ": Finding exit status for thread handle %p id %ld", handle,
330                   thread_private_handle->thread->id);
331 #endif
332
333         if(exitcode==NULL) {
334 #ifdef DEBUG
335                 g_message(G_GNUC_PRETTY_FUNCTION
336                           ": Nowhere to store exit code");
337 #endif
338                 return(FALSE);
339         }
340         
341         if(thread_handle->state!=THREAD_STATE_EXITED) {
342 #ifdef DEBUG
343                 g_message(G_GNUC_PRETTY_FUNCTION
344                           ": Thread still active (state %d, exited is %d)",
345                           thread_handle->state, THREAD_STATE_EXITED);
346 #endif
347                 *exitcode=STILL_ACTIVE;
348                 return(TRUE);
349         }
350         
351         *exitcode=thread_handle->exitstatus;
352         
353         return(TRUE);
354 }
355
356 /**
357  * GetCurrentThreadId:
358  *
359  * Looks up the thread ID of the current thread.  This ID can be
360  * passed to OpenThread() to create a new handle on this thread.
361  *
362  * Return value: the thread ID.
363  */
364 guint32 GetCurrentThreadId(void)
365 {
366         pthread_t tid=pthread_self();
367         
368         return(tid);
369 }
370
371 /**
372  * GetCurrentThread:
373  *
374  * Looks up the handle associated with the current thread.  Under
375  * Windows this is a pseudohandle, and must be duplicated with
376  * DuplicateHandle() for some operations.
377  *
378  * Return value: The current thread handle, or %NULL on failure.
379  * (Unknown whether Windows has a possible failure here.  It may be
380  * necessary to implement the pseudohandle-constant behaviour).
381  */
382 gpointer GetCurrentThread(void)
383 {
384         gpointer ret=NULL;
385         guint32 tid;
386         
387         tid=GetCurrentThreadId();
388         
389         mono_mutex_lock(&thread_hash_mutex);
390
391         ret=g_hash_table_lookup(thread_hash, &tid);
392         
393         mono_mutex_unlock(&thread_hash_mutex);
394         
395         return(ret);
396 }
397
398 /**
399  * ResumeThread:
400  * @handle: the thread handle to resume
401  *
402  * Decrements the suspend count of thread @handle. A thread can only
403  * run if its suspend count is zero.
404  *
405  * Return value: the previous suspend count, or 0xFFFFFFFF on error.
406  */
407 guint32 ResumeThread(gpointer handle G_GNUC_UNUSED)
408 {
409         return(0xFFFFFFFF);
410 }
411
412 /**
413  * SuspendThread:
414  * @handle: the thread handle to suspend
415  *
416  * Increments the suspend count of thread @handle. A thread can only
417  * run if its suspend count is zero.
418  *
419  * Return value: the previous suspend count, or 0xFFFFFFFF on error.
420  */
421 guint32 SuspendThread(gpointer handle G_GNUC_UNUSED)
422 {
423         return(0xFFFFFFFF);
424 }
425
426 /*
427  * We assume here that TLS_MINIMUM_AVAILABLE is less than
428  * PTHREAD_KEYS_MAX, allowing enough overhead for a few TLS keys for
429  * library usage.
430  *
431  * Currently TLS_MINIMUM_AVAILABLE is 64 and _POSIX_THREAD_KEYS_MAX
432  * (the minimum value for PTHREAD_KEYS_MAX) is 128, so we should be
433  * fine.
434  */
435
436 static pthread_key_t TLS_keys[TLS_MINIMUM_AVAILABLE];
437 static gboolean TLS_used[TLS_MINIMUM_AVAILABLE]={FALSE};
438 #ifdef TLS_PTHREAD_MUTEX
439 static mono_mutex_t TLS_mutex=MONO_MUTEX_INITIALIZER;
440 #else
441 static guint32 TLS_spinlock=0;
442 #endif
443
444 /**
445  * TlsAlloc:
446  *
447  * Allocates a Thread Local Storage (TLS) index.  Any thread in the
448  * same process can use this index to store and retrieve values that
449  * are local to that thread.
450  *
451  * Return value: The index value, or %TLS_OUT_OF_INDEXES if no index
452  * is available.
453  */
454 guint32 TlsAlloc(void)
455 {
456         guint32 i;
457         
458 #ifdef TLS_PTHREAD_MUTEX
459         mono_mutex_lock(&TLS_mutex);
460 #else
461         MONO_SPIN_LOCK (TLS_spinlock);
462 #endif
463         
464         for(i=0; i<TLS_MINIMUM_AVAILABLE; i++) {
465                 if(TLS_used[i]==FALSE) {
466                         TLS_used[i]=TRUE;
467                         pthread_key_create(&TLS_keys[i], NULL);
468
469 #ifdef TLS_PTHREAD_MUTEX
470                         mono_mutex_unlock(&TLS_mutex);
471 #else
472                         MONO_SPIN_UNLOCK (TLS_spinlock);
473 #endif
474         
475 #ifdef TLS_DEBUG
476                         g_message (G_GNUC_PRETTY_FUNCTION ": returning key %d",
477                                    i);
478 #endif
479                         
480                         return(i);
481                 }
482         }
483
484 #ifdef TLS_PTHREAD_MUTEX
485         mono_mutex_unlock(&TLS_mutex);
486 #else
487         MONO_SPIN_UNLOCK (TLS_spinlock);
488 #endif
489         
490 #ifdef TLS_DEBUG
491         g_message (G_GNUC_PRETTY_FUNCTION ": out of indices");
492 #endif
493                         
494         
495         return(TLS_OUT_OF_INDEXES);
496 }
497
498 #define MAKE_GC_ID(idx) (GUINT_TO_POINTER((idx)|(GetCurrentThreadId()<<8)))
499
500 /**
501  * TlsFree:
502  * @idx: The TLS index to free
503  *
504  * Releases a TLS index, making it available for reuse.  This call
505  * will delete any TLS data stored under index @idx in all threads.
506  *
507  * Return value: %TRUE on success, %FALSE otherwise.
508  */
509 gboolean TlsFree(guint32 idx)
510 {
511 #ifdef TLS_DEBUG
512         g_message (G_GNUC_PRETTY_FUNCTION ": freeing key %d", idx);
513 #endif
514
515 #ifdef TLS_PTHREAD_MUTEX
516         mono_mutex_lock(&TLS_mutex);
517 #else
518         MONO_SPIN_LOCK (TLS_spinlock);
519 #endif
520         
521         if(TLS_used[idx]==FALSE) {
522 #ifdef TLS_PTHREAD_MUTEX
523                 mono_mutex_unlock(&TLS_mutex);
524 #else
525                 MONO_SPIN_UNLOCK (TLS_spinlock);
526 #endif
527                 return(FALSE);
528         }
529         
530         TLS_used[idx]=FALSE;
531         pthread_key_delete(TLS_keys[idx]);
532         
533 #if HAVE_BOEHM_GC
534         mono_g_hash_table_remove (tls_gc_hash, MAKE_GC_ID (idx));
535 #endif
536
537 #ifdef TLS_PTHREAD_MUTEX
538         mono_mutex_unlock(&TLS_mutex);
539 #else
540         MONO_SPIN_UNLOCK (TLS_spinlock);
541 #endif
542         
543         return(TRUE);
544 }
545
546 /**
547  * TlsGetValue:
548  * @idx: The TLS index to retrieve
549  *
550  * Retrieves the TLS data stored under index @idx.
551  *
552  * Return value: The value stored in the TLS index @idx in the current
553  * thread, or %NULL on error.  As %NULL can be a valid return value,
554  * in this case GetLastError() returns %ERROR_SUCCESS.
555  */
556 gpointer TlsGetValue(guint32 idx)
557 {
558         gpointer ret;
559         
560 #ifdef TLS_DEBUG
561         g_message (G_GNUC_PRETTY_FUNCTION ": looking up key %d", idx);
562 #endif
563
564 #ifdef TLS_PTHREAD_MUTEX
565         mono_mutex_lock(&TLS_mutex);
566 #else
567         MONO_SPIN_LOCK (TLS_spinlock);
568 #endif
569         
570         if(TLS_used[idx]==FALSE) {
571 #ifdef TLS_DEBUG
572                 g_message (G_GNUC_PRETTY_FUNCTION ": key %d unused", idx);
573 #endif
574
575 #ifdef TLS_PTHREAD_MUTEX
576                 mono_mutex_unlock(&TLS_mutex);
577 #else
578                 MONO_SPIN_UNLOCK (TLS_spinlock);
579 #endif
580                 return(NULL);
581         }
582         
583         ret=pthread_getspecific(TLS_keys[idx]);
584
585 #ifdef TLS_DEBUG
586         g_message (G_GNUC_PRETTY_FUNCTION ": returning %p", ret);
587 #endif
588         
589 #ifdef TLS_PTHREAD_MUTEX
590         mono_mutex_unlock(&TLS_mutex);
591 #else
592         MONO_SPIN_UNLOCK (TLS_spinlock);
593 #endif
594         
595         return(ret);
596 }
597
598 /**
599  * TlsSetValue:
600  * @idx: The TLS index to store
601  * @value: The value to store under index @idx
602  *
603  * Stores @value at TLS index @idx.
604  *
605  * Return value: %TRUE on success, %FALSE otherwise.
606  */
607 gboolean TlsSetValue(guint32 idx, gpointer value)
608 {
609         int ret;
610
611 #ifdef TLS_DEBUG
612         g_message (G_GNUC_PRETTY_FUNCTION ": setting key %d to %p", idx,
613                    value);
614 #endif
615         
616 #ifdef TLS_PTHREAD_MUTEX
617         mono_mutex_lock(&TLS_mutex);
618 #else
619         MONO_SPIN_LOCK (TLS_spinlock);
620 #endif
621         
622         if(TLS_used[idx]==FALSE) {
623 #ifdef TLS_DEBUG
624                 g_message (G_GNUC_PRETTY_FUNCTION ": key %d unused", idx);
625 #endif
626
627 #ifdef TLS_PTHREAD_MUTEX
628                 mono_mutex_unlock(&TLS_mutex);
629 #else
630                 MONO_SPIN_UNLOCK (TLS_spinlock);
631 #endif
632                 return(FALSE);
633         }
634         
635         ret=pthread_setspecific(TLS_keys[idx], value);
636         if(ret!=0) {
637 #ifdef TLS_DEBUG
638                 g_message (G_GNUC_PRETTY_FUNCTION
639                            ": pthread_setspecific error: %s", strerror (ret));
640 #endif
641
642 #ifdef TLS_PTHREAD_MUTEX
643                 mono_mutex_unlock(&TLS_mutex);
644 #else
645                 MONO_SPIN_UNLOCK (TLS_spinlock);
646 #endif
647                 return(FALSE);
648         }
649         
650 #if HAVE_BOEHM_GC
651         if (!tls_gc_hash)
652                 tls_gc_hash = mono_g_hash_table_new(g_direct_hash, g_direct_equal);
653         mono_g_hash_table_insert (tls_gc_hash, MAKE_GC_ID (idx), value);
654 #endif
655
656 #ifdef TLS_PTHREAD_MUTEX
657         mono_mutex_unlock(&TLS_mutex);
658 #else
659         MONO_SPIN_UNLOCK (TLS_spinlock);
660 #endif
661         
662         return(TRUE);
663 }
664
665 /**
666  * Sleep:
667  * @ms: The time in milliseconds to suspend for
668  *
669  * Suspends execution of the current thread for @ms milliseconds.  A
670  * value of zero causes the thread to relinquish its time slice.  A
671  * value of %INFINITE causes an infinite delay.
672  */
673 void Sleep(guint32 ms)
674 {
675         struct timespec req, rem;
676         div_t divvy;
677         int ret;
678         
679 #ifdef DEBUG
680         g_message(G_GNUC_PRETTY_FUNCTION ": Sleeping for %d ms", ms);
681 #endif
682
683         if(ms==0) {
684                 sched_yield();
685                 return;
686         }
687         
688         /* FIXME: check for INFINITE and sleep forever */
689         divvy=div((int)ms, 1000);
690         
691         req.tv_sec=divvy.quot;
692         req.tv_nsec=divvy.rem*1000000;
693         
694 again:
695         ret=nanosleep(&req, &rem);
696         if(ret==-1) {
697                 /* Sleep interrupted with rem time remaining */
698 #ifdef DEBUG
699                 guint32 rems=rem.tv_sec*1000 + rem.tv_nsec/1000000;
700                 
701                 g_message(G_GNUC_PRETTY_FUNCTION ": Still got %d ms to go",
702                           rems);
703 #endif
704                 req=rem;
705                 goto again;
706         }
707 }
708
709 /* FIXME: implement alertable */
710 void SleepEx(guint32 ms, gboolean alertable)
711 {
712         if(alertable==TRUE) {
713                 g_warning(G_GNUC_PRETTY_FUNCTION ": alertable not implemented");
714         }
715         
716         Sleep(ms);
717 }