[xbuild] Fix test on windows.
[mono.git] / libgc / pthread_support.c
index 8b47b4d3db8666db8a7a2b47b3c3e9bf6e8ef9f9..3e588ace211abdcd0ed62b104f94f2a57d8b336b 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
  * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
  * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
- * Copyright (c) 2000-2001 by Hewlett-Packard Company.  All rights reserved.
+ * Copyright (c) 2000-2004 by Hewlett-Packard Company.  All rights reserved.
  *
  * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
  * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
@@ -50,8 +50,7 @@
 # include "private/pthread_support.h"
 
 # if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) \
-     && !defined(GC_IRIX_THREADS) && !defined(GC_WIN32_THREADS) \
-     && !defined(GC_AIX_THREADS)
+     && !defined(GC_WIN32_THREADS)
 
 # if defined(GC_HPUX_THREADS) && !defined(USE_PTHREAD_SPECIFIC) \
      && !defined(USE_COMPILER_TLS)
@@ -68,7 +67,9 @@
 # endif
 
 # if (defined(GC_DGUX386_THREADS) || defined(GC_OSF1_THREADS) || \
-      defined(GC_DARWIN_THREADS)) && !defined(USE_PTHREAD_SPECIFIC)
+      defined(GC_DARWIN_THREADS) || defined(GC_AIX_THREADS)) || \
+      defined(GC_NETBSD_THREADS) && !defined(USE_PTHREAD_SPECIFIC) || \
+      defined(GC_OPENBSD_THREADS)
 #   define USE_PTHREAD_SPECIFIC
 # endif
 
 #   if !defined(USE_PTHREAD_SPECIFIC) && !defined(USE_COMPILER_TLS)
 #     include "private/specific.h"
 #   endif
+
+/* Note that these macros should be used only to get/set the GC_thread pointer.
+ * We need to use both tls and pthread because we use the pthread_create function hook to
+ * free the data for foreign threads. When that doesn't happen, libgc could have old
+ * pthread_t that get reused...
+ */
 #   if defined(USE_PTHREAD_SPECIFIC)
 #     define GC_getspecific pthread_getspecific
 #     define GC_setspecific pthread_setspecific
       typedef pthread_key_t GC_key_t;
 #   endif
 #   if defined(USE_COMPILER_TLS)
-#     define GC_getspecific(x) (x)
-#     define GC_setspecific(key, v) ((key) = (v), 0)
-#     define GC_key_create(key, d) 0
-      typedef void * GC_key_t;
+/* Note sles9 gcc on powerpc gets confused by the define to set GC_thread_tls and pthread_setspecific
+ * so we actually use a static inline function decalred below that is equivalent to:
+ *   define GC_setspecific(key, v) (GC_thread_tls = (v), pthread_setspecific ((key), (v)))
+ */
+#     define GC_getspecific(x) (GC_thread_tls)
+#     define GC_key_create pthread_key_create
+      typedef pthread_key_t GC_key_t;
 #   endif
 # endif
 # include <stdlib.h>
 # include <semaphore.h>
 #endif /* !GC_DARWIN_THREADS */
 
-#if defined(GC_DARWIN_THREADS)
+#if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS)
 # include <sys/sysctl.h>
 #endif /* GC_DARWIN_THREADS */
 
+#if defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS)
+# include <sys/param.h>
+# include <sys/sysctl.h>
+#endif
+
 
 
 #if defined(GC_DGUX386_THREADS)
 #   endif
 #   undef pthread_join
 #   undef pthread_detach
+#   if defined(NACL)
+#     undef pthread_exit
+#   endif
 #   if defined(GC_OSF1_THREADS) && defined(_PTHREAD_USE_MANGLED_NAMES_) \
        && !defined(_PTHREAD_USE_PTDNAM_)
 /* Restore the original mangled names on Tru64 UNIX.  */
@@ -168,14 +186,39 @@ void GC_init_parallel();
 
 /* We don't really support thread-local allocation with DBG_HDRS_ALL */
 
+/* work around a dlopen issue (bug #75390), undefs to avoid warnings with redefinitions */
+#undef PACKAGE_BUGREPORT
+#undef PACKAGE_NAME
+#undef PACKAGE_STRING
+#undef PACKAGE_TARNAME
+#undef PACKAGE_VERSION
+#include "mono/utils/mono-compiler.h"
+
 static
+GC_key_t GC_thread_key;
+
 #ifdef USE_COMPILER_TLS
-  __thread
+__thread MONO_TLS_FAST void* GC_thread_tls;
+
+/*
+ * gcc errors out with /tmp/ccdPMFuq.s:2994: Error: symbol `.LTLS4' is already defined
+ * if the inline is added on powerpc
+ */
+#if !defined(__ppc__) && !defined(__powerpc__)
+inline
+#endif
+static int GC_setspecific (GC_key_t key, void *value) {
+       GC_thread_tls = value;
+       return pthread_setspecific (key, value);
+}
 #endif
-GC_key_t GC_thread_key;
 
 static GC_bool keys_initialized;
 
+#ifdef MONO_DEBUGGER_SUPPORTED
+#include "include/libgc-mono-debugger.h"
+#endif
+
 /* Recover the contents of the freelist array fl into the global one gfl.*/
 /* Note that the indexing scheme differs, in that gfl has finer size   */
 /* resolution, even if not all entries are used.                       */
@@ -213,6 +256,24 @@ static void return_freelists(ptr_t *fl, ptr_t *gfl)
 /* we arrange for those to fault asap.)                                        */
 static ptr_t size_zero_object = (ptr_t)(&size_zero_object);
 
+void GC_delete_gc_thread(pthread_t id, GC_thread gct);
+void GC_destroy_thread_local(GC_thread p);
+
+void GC_thread_deregister_foreign (void *data)
+{
+    GC_thread me = (GC_thread)data;
+ /*   GC_fprintf1( "\n\n\n\n --- Deregister %x ---\n\n\n\n\n", me->flags ); */
+    if (me -> flags & FOREIGN_THREAD) {
+       LOCK();
+ /*    GC_fprintf0( "\n\n\n\n --- FOO ---\n\n\n\n\n" ); */
+#if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
+       GC_destroy_thread_local (me);
+#endif
+       GC_delete_gc_thread(me->id, me);
+       UNLOCK();
+    }
+}
+
 /* Each thread structure must be initialized.  */
 /* This call must be made from the new thread. */
 /* Caller holds allocation lock.               */
@@ -221,7 +282,7 @@ void GC_init_thread_local(GC_thread p)
     int i;
 
     if (!keys_initialized) {
-       if (0 != GC_key_create(&GC_thread_key, 0)) {
+       if (0 != GC_key_create(&GC_thread_key, GC_thread_deregister_foreign)) {
            ABORT("Failed to create key for local allocator");
         }
        keys_initialized = TRUE;
@@ -556,6 +617,27 @@ GC_bool GC_thr_initialized = FALSE;
 
 volatile GC_thread GC_threads[THREAD_TABLE_SZ];
 
+/* 
+ * gcc-3.3.6 miscompiles the &GC_thread_key+sizeof(&GC_thread_key) expression so
+ * put it into a separate function.
+ */
+#   if defined(__GNUC__) && defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
+static __attribute__((noinline)) unsigned char* get_gc_thread_key_addr GC_PROTO((void))
+{
+       return (unsigned char*)&GC_thread_key;
+}
+
+void GC_push_thread_structures GC_PROTO((void))
+{
+    GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
+#   if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
+      GC_push_all((ptr_t)get_gc_thread_key_addr(),
+         (ptr_t)(get_gc_thread_key_addr())+sizeof(&GC_thread_key));
+#   endif
+}
+
+#else
+
 void GC_push_thread_structures GC_PROTO((void))
 {
     GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
@@ -565,6 +647,8 @@ void GC_push_thread_structures GC_PROTO((void))
 #   endif
 }
 
+#endif
+
 #ifdef THREAD_LOCAL_ALLOC
 /* We must explicitly mark ptrfree and gcj free lists, since the free  */
 /* list links wouldn't otherwise be found.  We also set them in the    */
@@ -595,6 +679,52 @@ void GC_mark_thread_local_free_lists(void)
 
 static struct GC_Thread_Rep first_thread;
 
+#ifdef NACL
+extern int nacl_thread_parked[MAX_NACL_GC_THREADS];
+extern int nacl_thread_used[MAX_NACL_GC_THREADS];
+extern int nacl_thread_parking_inited;
+extern int nacl_num_gc_threads;
+extern pthread_mutex_t nacl_thread_alloc_lock;
+extern __thread int nacl_thread_idx;
+extern __thread GC_thread nacl_gc_thread_self;
+
+void nacl_initialize_gc_thread()
+{
+    int i;
+    pthread_mutex_lock(&nacl_thread_alloc_lock);
+    if (!nacl_thread_parking_inited)
+    {
+        for (i = 0; i < MAX_NACL_GC_THREADS; i++) {
+            nacl_thread_used[i] = 0;
+            nacl_thread_parked[i] = 0;
+        }
+        nacl_thread_parking_inited = 1;
+    }
+    GC_ASSERT(nacl_num_gc_threads <= MAX_NACL_GC_THREADS);
+    for (i = 0; i < MAX_NACL_GC_THREADS; i++) {
+        if (nacl_thread_used[i] == 0) {
+            nacl_thread_used[i] = 1;
+            nacl_thread_idx = i;
+            nacl_num_gc_threads++;
+            break;
+        }
+    }
+    pthread_mutex_unlock(&nacl_thread_alloc_lock);
+}
+
+void nacl_shutdown_gc_thread()
+{
+    pthread_mutex_lock(&nacl_thread_alloc_lock);
+    GC_ASSERT(nacl_thread_idx >= 0 && nacl_thread_idx < MAX_NACL_GC_THREADS);
+    GC_ASSERT(nacl_thread_used[nacl_thread_idx] != 0);
+    nacl_thread_used[nacl_thread_idx] = 0;
+    nacl_thread_idx = -1;
+    nacl_num_gc_threads--;
+    pthread_mutex_unlock(&nacl_thread_alloc_lock);
+}
+
+#endif /* NACL */
+
 /* Add a thread to GC_threads.  We assume it wasn't already there.     */
 /* Caller holds allocation lock.                                       */
 GC_thread GC_new_thread(pthread_t id)
@@ -612,8 +742,15 @@ GC_thread GC_new_thread(pthread_t id)
     }
     if (result == 0) return(0);
     result -> id = id;
+#ifdef PLATFORM_ANDROID
+    result -> kernel_id = gettid();
+#endif
     result -> next = GC_threads[hv];
     GC_threads[hv] = result;
+#ifdef NACL
+    nacl_gc_thread_self = result;
+    nacl_initialize_gc_thread();
+#endif
     GC_ASSERT(result -> flags == 0 && result -> thread_blocked == 0);
     return(result);
 }
@@ -627,6 +764,11 @@ void GC_delete_thread(pthread_t id)
     register GC_thread p = GC_threads[hv];
     register GC_thread prev = 0;
     
+#ifdef NACL
+    nacl_shutdown_gc_thread();
+    nacl_gc_thread_self = NULL;
+#endif
+
     while (!pthread_equal(p -> id, id)) {
         prev = p;
         p = p -> next;
@@ -636,6 +778,15 @@ void GC_delete_thread(pthread_t id)
     } else {
         prev -> next = p -> next;
     }
+#ifdef MONO_DEBUGGER_SUPPORTED
+    if (gc_thread_vtable && gc_thread_vtable->thread_exited)
+       gc_thread_vtable->thread_exited (id, &p->stop_info.stack_ptr);
+#endif
+       
+#ifdef GC_DARWIN_THREADS
+       mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread);
+#endif
+       
     GC_INTERNAL_FREE(p);
 }
 
@@ -658,6 +809,11 @@ void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
     } else {
         prev -> next = p -> next;
     }
+       
+#ifdef GC_DARWIN_THREADS
+       mach_port_deallocate(mach_task_self(), p->stop_info.mach_thread);
+#endif
+       
     GC_INTERNAL_FREE(p);
 }
 
@@ -711,11 +867,12 @@ void GC_remove_all_threads_but_me(void)
              GC_destroy_thread_local(p);
            }
 #        endif /* THREAD_LOCAL_ALLOC */
-         if (p != &first_thread) GC_INTERNAL_FREE(p);
+           if (p != &first_thread) GC_INTERNAL_FREE(p);
        }
       }
       GC_threads[hv] = me;
     }
+    GC_INTERNAL_FREE(p);
 }
 #endif /* HANDLE_FORK */
 
@@ -892,9 +1049,9 @@ int GC_get_nprocs()
 /* We hold the allocation lock.        */
 void GC_thr_init()
 {
-#      ifndef GC_DARWIN_THREADS
-        int dummy;
-#      endif
+#   ifndef GC_DARWIN_THREADS
+      int dummy;
+#   endif
     GC_thread t;
 
     if (GC_thr_initialized) return;
@@ -913,6 +1070,14 @@ void GC_thr_init()
          t -> stop_info.stack_ptr = (ptr_t)(&dummy);
 #     endif
       t -> flags = DETACHED | MAIN_THREAD;
+#ifdef MONO_DEBUGGER_SUPPORTED
+      if (gc_thread_vtable && gc_thread_vtable->thread_created)
+#     ifdef GC_DARWIN_THREADS
+        gc_thread_vtable->thread_created (mach_thread_self (), &t->stop_info.stack_ptr);
+#     else
+         gc_thread_vtable->thread_created (pthread_self (), &t->stop_info.stack_ptr);
+#     endif
+#endif
 
     GC_stop_init();
 
@@ -926,14 +1091,15 @@ void GC_thr_init()
 #       if defined(GC_HPUX_THREADS)
          GC_nprocs = pthread_num_processors_np();
 #       endif
-#      if defined(GC_OSF1_THREADS)
+#      if defined(GC_OSF1_THREADS) || defined(GC_AIX_THREADS)
          GC_nprocs = sysconf(_SC_NPROCESSORS_ONLN);
          if (GC_nprocs <= 0) GC_nprocs = 1;
 #      endif
-#       if defined(GC_FREEBSD_THREADS)
-          GC_nprocs = 1;
+#       if defined(GC_IRIX_THREADS)
+         GC_nprocs = sysconf(_SC_NPROC_ONLN);
+         if (GC_nprocs <= 0) GC_nprocs = 1;
 #       endif
-#       if defined(GC_DARWIN_THREADS)
+#       if defined(GC_DARWIN_THREADS) || defined(GC_FREEBSD_THREADS) || defined(GC_NETBSD_THREADS) || defined(GC_OPENBSD_THREADS)
          int ncpus = 1;
          size_t len = sizeof(ncpus);
          sysctl((int[2]) {CTL_HW, HW_NCPU}, 2, &ncpus, &len, NULL, 0);
@@ -957,6 +1123,8 @@ void GC_thr_init()
              GC_markers = atoi(markers_string);
            } else {
              GC_markers = GC_nprocs;
+                 if (GC_markers > MAX_MARKERS)
+                         GC_markers = MAX_MARKERS;
            }
           }
 #      endif
@@ -980,6 +1148,8 @@ void GC_thr_init()
        /* Disable true incremental collection, but generational is OK. */
        GC_time_limit = GC_TIME_UNLIMITED;
       }
+      /* If we are using a parallel marker, actually start helper threads.  */
+        if (GC_parallel) start_mark_threads();
 #   endif
 }
 
@@ -996,10 +1166,6 @@ void GC_init_parallel()
 
     /* GC_init() calls us back, so set flag first.     */
     if (!GC_is_initialized) GC_init();
-    /* If we are using a parallel marker, start the helper threads.  */
-#     ifdef PARALLEL_MARK
-        if (GC_parallel) start_mark_threads();
-#     endif
     /* Initialize thread local free lists if used.     */
 #   if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
       LOCK();
@@ -1009,7 +1175,8 @@ void GC_init_parallel()
 }
 
 
-#if !defined(GC_DARWIN_THREADS)
+#if !defined(GC_DARWIN_THREADS) && !defined(GC_OPENBSD_THREADS)
+#ifndef NACL
 int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, sigset_t *oset)
 {
     sigset_t fudged_set;
@@ -1021,6 +1188,7 @@ int WRAP_FUNC(pthread_sigmask)(int how, const sigset_t *set, sigset_t *oset)
     }
     return(REAL_FUNC(pthread_sigmask)(how, set, oset));
 }
+#endif
 #endif /* !GC_DARWIN_THREADS */
 
 /* Wrappers for functions that are likely to block for an appreciable  */
@@ -1102,6 +1270,11 @@ void GC_thread_exit_proc(void *arg)
     me = GC_lookup_thread(pthread_self());
     GC_destroy_thread_local(me);
     if (me -> flags & DETACHED) {
+# ifdef THREAD_LOCAL_ALLOC
+               /* NULL out the tls key to prevent the dtor function from being called */
+               if (0 != GC_setspecific(GC_thread_key, NULL))
+                       ABORT("Failed to set thread specific allocation pointers");
+#endif
        GC_delete_thread(pthread_self());
     } else {
        me -> flags |= FINISHED;
@@ -1146,6 +1319,17 @@ int WRAP_FUNC(pthread_join)(pthread_t thread, void **retval)
     return result;
 }
 
+#ifdef NACL
+/* Native Client doesn't support pthread cleanup functions, */
+/* so wrap pthread_exit and manually cleanup the thread.    */
+void
+WRAP_FUNC(pthread_exit)(void *status)
+{
+    GC_thread_exit_proc(0); 
+    REAL_FUNC(pthread_exit)(status);
+}
+#endif
+
 int
 WRAP_FUNC(pthread_detach)(pthread_t thread)
 {
@@ -1170,15 +1354,14 @@ WRAP_FUNC(pthread_detach)(pthread_t thread)
 
 GC_bool GC_in_thread_creation = FALSE;
 
-void * GC_start_routine(void * arg)
+typedef void *(*ThreadStartFn)(void *);
+void * GC_start_routine_head(void * arg, void *base_addr,
+                            ThreadStartFn *start, void **start_arg )
 {
-    int dummy;
     struct start_info * si = arg;
     void * result;
     GC_thread me;
     pthread_t my_pthread;
-    void *(*start)(void *);
-    void *start_arg;
 
     my_pthread = pthread_self();
 #   ifdef DEBUG_THREADS
@@ -1201,7 +1384,7 @@ void * GC_start_routine(void * arg)
     /* one for the main thread.  There is a strong argument that that's        */
     /* a kernel bug, but a pervasive one.                              */
 #   ifdef STACK_GROWS_DOWN
-      me -> stack_end = (ptr_t)(((word)(&dummy) + (GC_page_size - 1))
+      me -> stack_end = (ptr_t)(((word)(base_addr) + (GC_page_size - 1))
                                & ~(GC_page_size - 1));
 #        ifndef GC_DARWIN_THREADS
         me -> stop_info.stack_ptr = me -> stack_end - 0x10;
@@ -1209,7 +1392,7 @@ void * GC_start_routine(void * arg)
        /* Needs to be plausible, since an asynchronous stack mark      */
        /* should not crash.                                            */
 #   else
-      me -> stack_end = (ptr_t)((word)(&dummy) & ~(GC_page_size - 1));
+      me -> stack_end = (ptr_t)((word)(base_addr) & ~(GC_page_size - 1));
       me -> stop_info.stack_ptr = me -> stack_end + 0x10;
 #   endif
     /* This is dubious, since we may be more than a page into the stack, */
@@ -1220,20 +1403,68 @@ void * GC_start_routine(void * arg)
       /* This is also < 100% convincing.  We should also read this     */
       /* from /proc, but the hook to do so isn't there yet.            */
 #   endif /* IA64 */
+#ifdef MONO_DEBUGGER_SUPPORTED
+    if (gc_thread_vtable && gc_thread_vtable->thread_created)
+#      ifdef GC_DARWIN_THREADS
+       gc_thread_vtable->thread_created (mach_thread_self(), &me->stop_info.stack_ptr);
+#      else
+       gc_thread_vtable->thread_created (my_pthread, &me->stop_info.stack_ptr);
+#      endif
+#endif
     UNLOCK();
-    start = si -> start_routine;
-#   ifdef DEBUG_THREADS
-       GC_printf1("start_routine = 0x%lx\n", start);
-#   endif
-    start_arg = si -> arg;
-    sem_post(&(si -> registered));     /* Last action on si.   */
+
+    if (start) *start = si -> start_routine;
+    if (start_arg) *start_arg = si -> arg;
+
+       if (!(si->flags & FOREIGN_THREAD))
+               sem_post(&(si -> registered));  /* Last action on si.   */
                                        /* OK to deallocate.    */
-    pthread_cleanup_push(GC_thread_exit_proc, 0);
 #   if defined(THREAD_LOCAL_ALLOC) && !defined(DBG_HDRS_ALL)
        LOCK();
         GC_init_thread_local(me);
        UNLOCK();
 #   endif
+
+    return me;
+}
+
+int GC_thread_register_foreign (void *base_addr)
+{
+    struct start_info si = { 0, }; /* stacked for legibility & locking */
+    GC_thread me;
+
+#   ifdef DEBUG_THREADS
+        GC_printf1( "GC_thread_register_foreign %p\n", &si );
+#   endif
+
+    si.flags = FOREIGN_THREAD;
+
+    if (!parallel_initialized) GC_init_parallel();
+    LOCK();
+    if (!GC_thr_initialized) GC_thr_init();
+
+    UNLOCK();
+
+    me = GC_start_routine_head(&si, base_addr, NULL, NULL);
+
+    return me != NULL;
+}
+
+void * GC_start_routine(void * arg)
+{
+    int dummy;
+    struct start_info * si = arg;
+    void * result;
+    GC_thread me;
+    ThreadStartFn start;
+    void *start_arg;
+
+    me = GC_start_routine_head (arg, &dummy, &start, &start_arg);
+
+    pthread_cleanup_push(GC_thread_exit_proc, 0);
+#   ifdef DEBUG_THREADS
+       GC_printf1("start_routine = 0x%lx\n", start);
+#   endif
     result = (*start)(start_arg);
 #if DEBUG_THREADS
         GC_printf1("Finishing thread 0x%x\n", pthread_self());
@@ -1275,7 +1506,7 @@ WRAP_FUNC(pthread_create)(pthread_t *new_thread,
     if (!GC_thr_initialized) GC_thr_init();
 #   ifdef GC_ASSERTIONS
       {
-       int stack_size;
+       size_t stack_size;
        if (NULL == attr) {
           pthread_attr_t my_attr;
           pthread_attr_init(&my_attr);
@@ -1283,7 +1514,13 @@ WRAP_FUNC(pthread_create)(pthread_t *new_thread,
        } else {
           pthread_attr_getstacksize(attr, &stack_size);
        }
-       GC_ASSERT(stack_size >= (8*HBLKSIZE*sizeof(word)));
+#       ifdef PARALLEL_MARK
+         GC_ASSERT(stack_size >= (8*HBLKSIZE*sizeof(word)));
+#       else
+          /* FreeBSD-5.3/Alpha: default pthread stack is 64K,  */
+         /* HBLKSIZE=8192, sizeof(word)=8                      */
+         GC_ASSERT(stack_size >= 65536);
+#       endif
        /* Our threads may need to do some work for the GC.     */
        /* Ridiculously small threads won't work, and they      */
        /* probably wouldn't work anyway.                       */