OSDN Git Service

Tue Jul 2 18:45:45 2002 J"orn Rennecke <joern.rennecke@superh.com>
[pf3gnuchains/gcc-fork.git] / boehm-gc / irix_threads.c
index f45c463..75b7c63 100644 (file)
@@ -1,6 +1,7 @@
 /* 
- * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
- * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1991-1995 by Xerox Corporation.  All rights reserved.
+ * Copyright (c) 1996-1999 by Silicon Graphics.  All rights reserved.
+ * Copyright (c) 1999 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.
  * not guaranteed by the Pthread standard.  It may or may not be portable
  * to other implementations.
  *
+ * This now also includes an initial attempt at thread support for
+ * HP/UX 11.
+ *
  * Note that there is a lot of code duplication between linux_threads.c
  * and irix_threads.c; any changes made here may need to be reflected
  * there too.
  */
 
-# if defined(IRIX_THREADS)
+# if defined(GC_IRIX_THREADS)
 
-# include "gc_priv.h"
+# include "private/gc_priv.h"
 # include <pthread.h>
+# include <semaphore.h>
 # include <time.h>
 # include <errno.h>
 # include <unistd.h>
@@ -34,6 +39,7 @@
 #undef pthread_create
 #undef pthread_sigmask
 #undef pthread_join
+#undef pthread_detach
 
 void GC_thr_init();
 
@@ -94,8 +100,8 @@ GC_thread GC_lookup_thread(pthread_t id);
  * The only way to suspend threads given the pthread interface is to send
  * signals.  Unfortunately, this means we have to reserve
  * a signal, and intercept client calls to change the signal mask.
+ * We use SIG_SUSPEND, defined in gc_priv.h.
  */
-# define SIG_SUSPEND (SIGRTMIN + 6)
 
 pthread_mutex_t GC_suspend_lock = PTHREAD_MUTEX_INITIALIZER;
                                /* Number of threads stopped so far     */
@@ -135,8 +141,6 @@ GC_bool GC_thr_initialized = FALSE;
 
 size_t GC_min_stack_sz;
 
-size_t GC_page_sz;
-
 # define N_FREE_LISTS 25
 ptr_t GC_stack_free_lists[N_FREE_LISTS] = { 0 };
                /* GC_stack_free_lists[i] is free list for stacks of    */
@@ -165,11 +169,15 @@ ptr_t GC_stack_alloc(size_t * stack_size)
     if (result != 0) {
         GC_stack_free_lists[index] = *(ptr_t *)result;
     } else {
-        result = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_sz);
-        result = (ptr_t)(((word)result + GC_page_sz) & ~(GC_page_sz - 1));
+        result = (ptr_t) GC_scratch_alloc(search_sz + 2*GC_page_size);
+        result = (ptr_t)(((word)result + GC_page_size) & ~(GC_page_size - 1));
         /* Protect hottest page to detect overflow. */
-        /* mprotect(result, GC_page_sz, PROT_NONE); */
-        result += GC_page_sz;
+#      ifdef STACK_GROWS_UP
+          /* mprotect(result + search_sz, GC_page_size, PROT_NONE); */
+#      else
+          /* mprotect(result, GC_page_size, PROT_NONE); */
+          result += GC_page_size;
+#      endif
     }
     *stack_size = search_sz;
     return(result);
@@ -195,6 +203,11 @@ void GC_stack_free(ptr_t stack, size_t size)
 # define THREAD_TABLE_SZ 128   /* Must be power of 2   */
 volatile GC_thread GC_threads[THREAD_TABLE_SZ];
 
+void GC_push_thread_structures GC_PROTO((void))
+{
+    GC_push_all((ptr_t)(GC_threads), (ptr_t)(GC_threads)+sizeof(GC_threads));
+}
+
 /* 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)
@@ -210,7 +223,7 @@ GC_thread GC_new_thread(pthread_t id)
        /* Dont acquire allocation lock, since we may already hold it. */
     } else {
         result = (struct GC_Thread_Rep *)
-                GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
+                GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep), NORMAL);
     }
     if (result == 0) return(0);
     result -> id = id;
@@ -374,13 +387,14 @@ int GC_is_thread_stack(ptr_t addr)
 }
 # endif
 
-/* We hold allocation lock.  We assume the world is stopped.   */
+/* We hold allocation lock.  Should do exactly the right thing if the  */
+/* world is stopped.  Should not fail if it isn't.                     */
 void GC_push_all_stacks()
 {
     register int i;
     register GC_thread p;
     register ptr_t sp = GC_approx_sp();
-    register ptr_t lo, hi;
+    register ptr_t hot, cold;
     pthread_t me = pthread_self();
     
     if (!GC_thr_initialized) GC_thr_init();
@@ -389,17 +403,25 @@ void GC_push_all_stacks()
       for (p = GC_threads[i]; p != 0; p = p -> next) {
         if (p -> flags & FINISHED) continue;
         if (pthread_equal(p -> id, me)) {
-           lo = GC_approx_sp();
+           hot = GC_approx_sp();
        } else {
-           lo = p -> stack_ptr;
+           hot = p -> stack_ptr;
        }
         if (p -> stack_size != 0) {
-            hi = p -> stack + p -> stack_size;
+#        ifdef STACK_GROWS_UP
+           cold = p -> stack;
+#        else
+            cold = p -> stack + p -> stack_size;
+#        endif
         } else {
             /* The original stack. */
-            hi = GC_stackbottom;
+            cold = GC_stackbottom;
         }
-        GC_push_all_stack(lo, hi);
+#      ifdef STACK_GROWS_UP
+          GC_push_all_stack(cold, hot);
+#      else
+          GC_push_all_stack(hot, cold);
+#      endif
       }
     }
 }
@@ -411,9 +433,9 @@ void GC_thr_init()
     GC_thread t;
     struct sigaction act;
 
+    if (GC_thr_initialized) return;
     GC_thr_initialized = TRUE;
     GC_min_stack_sz = HBLKSIZE;
-    GC_page_sz = sysconf(_SC_PAGESIZE);
     (void) sigaction(SIG_SUSPEND, 0, &act);
     if (act.sa_handler != SIG_DFL)
        ABORT("Previously installed SIG_SUSPEND handler");
@@ -445,9 +467,14 @@ int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
 struct start_info {
     void *(*start_routine)(void *);
     void *arg;
+    word flags;
+    ptr_t stack;
+    size_t stack_size;
+    sem_t registered;          /* 1 ==> in our thread table, but       */
+                               /* parent hasn't yet noticed.           */
 };
 
-void GC_thread_exit_proc(void *dummy)
+void GC_thread_exit_proc(void *arg)
 {
     GC_thread me;
 
@@ -472,10 +499,36 @@ int GC_pthread_join(pthread_t thread, void **retval)
     /* cant have been recycled by pthreads.                            */
     UNLOCK();
     result = pthread_join(thread, retval);
+    /* Some versions of the Irix pthreads library can erroneously      */
+    /* return EINTR when the call succeeds.                            */
+       if (EINTR == result) result = 0;
+    if (result == 0) {
+        LOCK();
+        /* Here the pthread thread id may have been recycled. */
+        GC_delete_gc_thread(thread, thread_gc_id);
+        UNLOCK();
+    }
+    return result;
+}
+
+int GC_pthread_detach(pthread_t thread)
+{
+    int result;
+    GC_thread thread_gc_id;
+    
     LOCK();
-    /* Here the pthread thread id may have been recycled. */
-    GC_delete_gc_thread(thread, thread_gc_id);
+    thread_gc_id = GC_lookup_thread(thread);
     UNLOCK();
+    result = pthread_detach(thread);
+    if (result == 0) {
+      LOCK();
+      thread_gc_id -> flags |= DETACHED;
+      /* Here the pthread thread id may have been recycled. */
+      if (thread_gc_id -> flags & FINISHED) {
+        GC_delete_gc_thread(thread, thread_gc_id);
+      }
+      UNLOCK();
+    }
     return result;
 }
 
@@ -484,12 +537,34 @@ void * GC_start_routine(void * arg)
     struct start_info * si = arg;
     void * result;
     GC_thread me;
-
+    pthread_t my_pthread;
+    void *(*start)(void *);
+    void *start_arg;
+
+    my_pthread = pthread_self();
+    /* If a GC occurs before the thread is registered, that GC will    */
+    /* ignore this thread.  That's fine, since it will block trying to  */
+    /* acquire the allocation lock, and won't yet hold interesting     */
+    /* pointers.                                                       */
     LOCK();
-    me = GC_lookup_thread(pthread_self());
+    /* We register the thread here instead of in the parent, so that   */
+    /* we don't need to hold the allocation lock during pthread_create. */
+    /* Holding the allocation lock there would make REDIRECT_MALLOC    */
+    /* impossible.  It probably still doesn't work, but we're a little  */
+    /* closer ...                                                      */
+    /* This unfortunately means that we have to be careful the parent  */
+    /* doesn't try to do a pthread_join before we're registered.       */
+    me = GC_new_thread(my_pthread);
+    me -> flags = si -> flags;
+    me -> stack = si -> stack;
+    me -> stack_size = si -> stack_size;
+    me -> stack_ptr = (ptr_t)si -> stack + si -> stack_size - sizeof(word);
     UNLOCK();
+    start = si -> start_routine;
+    start_arg = si -> arg;
+    sem_post(&(si -> registered));
     pthread_cleanup_push(GC_thread_exit_proc, 0);
-    result = (*(si -> start_routine))(si -> arg);
+    result = (*start)(start_arg);
     me -> status = result;
     me -> flags |= FINISHED;
     pthread_cleanup_pop(1);
@@ -499,6 +574,8 @@ void * GC_start_routine(void * arg)
     return(result);
 }
 
+# define copy_attr(pa_ptr, source) *(pa_ptr) = *(source)
+
 int
 GC_pthread_create(pthread_t *new_thread,
                  const pthread_attr_t *attr,
@@ -506,24 +583,28 @@ GC_pthread_create(pthread_t *new_thread,
 {
     int result;
     GC_thread t;
-    pthread_t my_new_thread;
     void * stack;
     size_t stacksize;
     pthread_attr_t new_attr;
     int detachstate;
     word my_flags = 0;
     struct start_info * si = GC_malloc(sizeof(struct start_info)); 
+       /* This is otherwise saved only in an area mmapped by the thread */
+       /* library, which isn't visible to the collector.                */
 
     if (0 == si) return(ENOMEM);
+    if (0 != sem_init(&(si -> registered), 0, 0)) {
+        ABORT("sem_init failed");
+    }
     si -> start_routine = start_routine;
     si -> arg = arg;
     LOCK();
-    if (!GC_thr_initialized) GC_thr_init();
+    if (!GC_is_initialized) GC_init();
     if (NULL == attr) {
         stack = 0;
        (void) pthread_attr_init(&new_attr);
     } else {
-        new_attr = *attr;
+       copy_attr(&new_attr, attr);
        pthread_attr_getstackaddr(&new_attr, &stack);
     }
     pthread_attr_getstacksize(&new_attr, &stacksize);
@@ -540,36 +621,43 @@ GC_pthread_create(pthread_t *new_thread,
        my_flags |= CLIENT_OWNS_STACK;
     }
     if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
-    result = pthread_create(&my_new_thread, &new_attr, GC_start_routine, si);
-    /* No GC can start until the thread is registered, since we hold   */
-    /* the allocation lock.                                            */
-    if (0 == result) {
-        t = GC_new_thread(my_new_thread);
-        t -> flags = my_flags;
-        t -> stack = stack;
-        t -> stack_size = stacksize;
-       t -> stack_ptr = (ptr_t)stack + stacksize - sizeof(word);
-        if (0 != new_thread) *new_thread = my_new_thread;
-    } else if (!(my_flags & CLIENT_OWNS_STACK)) {
+    si -> flags = my_flags;
+    si -> stack = stack;
+    si -> stack_size = stacksize;
+    result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
+    if (0 == new_thread && !(my_flags & CLIENT_OWNS_STACK)) {
        GC_stack_free(stack, stacksize);
     }        
     UNLOCK();  
-    /* pthread_attr_destroy(&new_attr); */
+    /* Wait until child has been added to the thread table.            */
+    /* This also ensures that we hold onto si until the child is done  */
+    /* with it.  Thus it doesn't matter whether it is otherwise                */
+    /* visible to the collector.                                       */
+        while (0 != sem_wait(&(si -> registered))) {
+         if (errno != EINTR) {
+           GC_printf1("Sem_wait: errno = %ld\n", (unsigned long) errno);
+           ABORT("sem_wait failed");
+         }
+       }
+        sem_destroy(&(si -> registered));
+    pthread_attr_destroy(&new_attr);  /* Probably unnecessary under Irix */
     return(result);
 }
 
-GC_bool GC_collecting = 0; /* A hint that we're in the collector and       */
+VOLATILE GC_bool GC_collecting = 0;
+                       /* A hint that we're in the collector and       */
                         /* holding the allocation lock for an           */
                         /* extended period.                             */
 
 /* Reasonably fast spin locks.  Basically the same implementation */
-/* as STL alloc.h.  This isn't really the right way to do this.   */
-/* but until the POSIX scheduling mess gets straightened out ...  */
-
-unsigned long GC_allocate_lock = 0;
+/* as STL alloc.h.                                               */
 
 #define SLEEP_THRESHOLD 3
 
+unsigned long GC_allocate_lock = 0;
+# define GC_TRY_LOCK() !GC_test_and_set(&GC_allocate_lock)
+# define GC_LOCK_TAKEN GC_allocate_lock
+
 void GC_lock()
 {
 #   define low_spin_max 30  /* spin cycles if we suspect uniprocessor */
@@ -582,7 +670,7 @@ void GC_lock()
 #   define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
     int i;
 
-    if (!GC_test_and_set(&GC_allocate_lock, 1)) {
+    if (GC_TRY_LOCK()) {
         return;
     }
     junk = 0;
@@ -590,11 +678,11 @@ void GC_lock()
     my_last_spins = last_spins;
     for (i = 0; i < my_spin_max; i++) {
         if (GC_collecting) goto yield;
-        if (i < my_last_spins/2 || GC_allocate_lock) {
+        if (i < my_last_spins/2 || GC_LOCK_TAKEN) {
             PAUSE; 
             continue;
         }
-        if (!GC_test_and_set(&GC_allocate_lock, 1)) {
+        if (GC_TRY_LOCK()) {
            /*
              * got it!
              * Spinning worked.  Thus we're probably not being scheduled
@@ -610,7 +698,7 @@ void GC_lock()
     spin_max = low_spin_max;
 yield:
     for (i = 0;; ++i) {
-        if (!GC_test_and_set(&GC_allocate_lock, 1)) {
+        if (GC_TRY_LOCK()) {
             return;
         }
         if (i < SLEEP_THRESHOLD) {
@@ -628,13 +716,11 @@ yield:
     }
 }
 
-
-
 # else
 
 #ifndef LINT
   int GC_no_Irix_threads;
 #endif
 
-# endif /* IRIX_THREADS */
+# endif /* GC_IRIX_THREADS */