2004-11-09 Roland McGrath <roland@redhat.com>
[kopensolaris-gnu/glibc.git] / nptl / pthread_create.c
index c0d9ec0..82a3c68 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+/* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
 #include <ldsodefs.h>
 #include <atomic.h>
 #include <libc-internal.h>
+#include <resolv.h>
 
 #include <shlib-compat.h>
 
 
 /* Local function to start thread and handle cleanup.  */
 static int start_thread (void *arg);
-/* Similar version used when debugging.  */
-static int start_thread_debug (void *arg);
 
 
 /* Nozero if debugging mode is enabled.  */
@@ -61,9 +60,6 @@ struct pthread_key_struct __pthread_keys[PTHREAD_KEYS_MAX]
   __attribute__ ((nocommon));
 hidden_data_def (__pthread_keys)
 
-/* This is for libthread_db only.  */
-const int __pthread_pthread_sizeof_descr = sizeof (struct pthread);
-
 struct pthread *
 internal_function
 __find_in_stack_list (pd)
@@ -106,73 +102,95 @@ __find_in_stack_list (pd)
 
 
 /* Deallocate POSIX thread-local-storage.  */
-static void
-internal_function
-deallocate_tsd (struct pthread *pd)
+void
+attribute_hidden
+__nptl_deallocate_tsd (void)
 {
+  struct pthread *self = THREAD_SELF;
+
   /* Maybe no data was ever allocated.  This happens often so we have
      a flag for this.  */
-  if (pd->specific_used)
+  if (THREAD_GETMEM (self, specific_used))
     {
       size_t round;
-      bool found_nonzero;
+      size_t cnt;
 
-      for (round = 0, found_nonzero = true;
-          found_nonzero && round < PTHREAD_DESTRUCTOR_ITERATIONS;
-          ++round)
+      round = 0;
+      do
        {
-         size_t cnt;
          size_t idx;
 
          /* So far no new nonzero data entry.  */
-         found_nonzero = false;
+         THREAD_SETMEM (self, specific_used, false);
 
          for (cnt = idx = 0; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
-           if (pd->specific[cnt] != NULL)
-             {
-               size_t inner;
-
-               for (inner = 0; inner < PTHREAD_KEY_2NDLEVEL_SIZE;
-                    ++inner, ++idx)
-                 {
-                   void *data = pd->specific[cnt][inner].data;
-
-                   if (data != NULL
-                       /* Make sure the data corresponds to a valid
-                          key.  This test fails if the key was
-                          deallocated and also if it was
-                          re-allocated.  It is the user's
-                          responsibility to free the memory in this
-                          case.  */
-                       && (pd->specific[cnt][inner].seq
-                           == __pthread_keys[idx].seq)
-                       /* It is not necessary to register a destructor
-                          function.  */
-                       && __pthread_keys[idx].destr != NULL)
-                     {
-                       pd->specific[cnt][inner].data = NULL;
-                       __pthread_keys[idx].destr (data);
-                       found_nonzero = true;
-                     }
-                 }
-
-               if (cnt != 0)
-                 {
-                   /* The first block is allocated as part of the thread
-                      descriptor.  */
-                   free (pd->specific[cnt]);
-                   pd->specific[cnt] = NULL;
-                 }
-               else
-                 /* Clear the memory of the first block for reuse.  */
-                 memset (&pd->specific_1stblock, '\0',
-                         sizeof (pd->specific_1stblock));
-             }
-           else
-             idx += PTHREAD_KEY_1STLEVEL_SIZE;
+           {
+             struct pthread_key_data *level2;
+
+             level2 = THREAD_GETMEM_NC (self, specific, cnt);
+
+             if (level2 != NULL)
+               {
+                 size_t inner;
+
+                 for (inner = 0; inner < PTHREAD_KEY_2NDLEVEL_SIZE;
+                      ++inner, ++idx)
+                   {
+                     void *data = level2[inner].data;
+
+                     if (data != NULL)
+                       {
+                         /* Always clear the data.  */
+                         level2[inner].data = NULL;
+
+                         /* Make sure the data corresponds to a valid
+                            key.  This test fails if the key was
+                            deallocated and also if it was
+                            re-allocated.  It is the user's
+                            responsibility to free the memory in this
+                            case.  */
+                         if (level2[inner].seq
+                             == __pthread_keys[idx].seq
+                             /* It is not necessary to register a destructor
+                                function.  */
+                             && __pthread_keys[idx].destr != NULL)
+                           /* Call the user-provided destructor.  */
+                           __pthread_keys[idx].destr (data);
+                       }
+                   }
+               }
+             else
+               idx += PTHREAD_KEY_1STLEVEL_SIZE;
+           }
+
+         if (THREAD_GETMEM (self, specific_used) == 0)
+           /* No data has been modified.  */
+           goto just_free;
        }
+      /* We only repeat the process a fixed number of times.  */
+      while (__builtin_expect (++round < PTHREAD_DESTRUCTOR_ITERATIONS, 0));
+
+      /* Just clear the memory of the first block for reuse.  */
+      memset (&THREAD_SELF->specific_1stblock, '\0',
+             sizeof (self->specific_1stblock));
+
+    just_free:
+      /* Free the memory for the other blocks.  */
+      for (cnt = 1; cnt < PTHREAD_KEY_1STLEVEL_SIZE; ++cnt)
+       {
+         struct pthread_key_data *level2;
 
-      pd->specific_used = false;
+         level2 = THREAD_GETMEM_NC (self, specific, cnt);
+         if (level2 != NULL)
+           {
+             /* The first block is allocated as part of the thread
+                descriptor.  */
+             free (level2);
+             THREAD_SETMEM_NC (self, specific, cnt, NULL);
+           }
+       }
+
+      THREAD_SETMEM (self, specific_used, false);
     }
 }
 
@@ -193,9 +211,6 @@ __free_tcb (struct pthread *pd)
           running thread is gone.  */
        abort ();
 
-      /* Run the destructor for the thread-local data.  */
-      deallocate_tsd (pd);
-
       /* Queue the stack memory block for reuse and exit the process.  The
         kernel will signal via writing to the address returned by
         QUEUE-STACK when the stack is available.  */
@@ -207,9 +222,6 @@ __free_tcb (struct pthread *pd)
 static int
 start_thread (void *arg)
 {
-  /* One more thread.  */
-  atomic_increment (&__nptl_nthreads);
-
   struct pthread *pd = (struct pthread *) arg;
 
 #if HP_TIMING_AVAIL
@@ -219,14 +231,47 @@ start_thread (void *arg)
   THREAD_SETMEM (pd, cpuclock_offset, now);
 #endif
 
+  /* Initialize resolver state pointer.  */
+  __resp = &pd->res;
+
   /* This is where the try/finally block should be created.  For
      compilers without that support we do use setjmp.  */
-  if (__builtin_expect (setjmp (pd->cancelbuf) == 0, 1))
+  struct pthread_unwind_buf unwind_buf;
+
+  /* No previous handlers.  */
+  unwind_buf.priv.data.prev = NULL;
+  unwind_buf.priv.data.cleanup = NULL;
+
+  int not_first_call;
+  not_first_call = setjmp ((struct __jmp_buf_tag *) unwind_buf.cancel_jmp_buf);
+  if (__builtin_expect (! not_first_call, 1))
     {
+      /* Store the new cleanup handler info.  */
+      THREAD_SETMEM (pd, cleanup_jmp_buf, &unwind_buf);
+
+      if (__builtin_expect (pd->stopped_start, 0))
+       {
+         int oldtype = CANCEL_ASYNC ();
+
+         /* Get the lock the parent locked to force synchronization.  */
+         lll_lock (pd->lock);
+         /* And give it up right away.  */
+         lll_unlock (pd->lock);
+
+         CANCEL_RESET (oldtype);
+       }
+
       /* Run the code the user provided.  */
+#ifdef CALL_THREAD_FCT
+      THREAD_SETMEM (pd, result, CALL_THREAD_FCT (pd));
+#else
       THREAD_SETMEM (pd, result, pd->start_routine (pd->arg));
+#endif
     }
 
+  /* Run the destructor for the thread-local data.  */
+  __nptl_deallocate_tsd ();
+
   /* Clean up any state libc stored in thread-local variables.  */
   __libc_thread_freeres ();
 
@@ -256,8 +301,8 @@ start_thread (void *arg)
 
              do
                pd->nextevent = __nptl_last_event;
-             while (atomic_compare_and_exchange_acq (&__nptl_last_event, pd,
-                                                     pd->nextevent) != 0);
+             while (atomic_compare_and_exchange_bool_acq (&__nptl_last_event,
+                                                          pd, pd->nextevent));
            }
 
          /* Now call the function to signal the event.  */
@@ -290,23 +335,6 @@ start_thread (void *arg)
 }
 
 
-/* Just list start_thread but we do some more things needed for a run
-   with a debugger attached.  */
-static int
-start_thread_debug (void *arg)
-{
-  struct pthread *pd = (struct pthread *) arg;
-
-  /* Get the lock the parent locked to force synchronization.  */
-  lll_lock (pd->lock);
-  /* And give it up right away.  */
-  lll_unlock (pd->lock);
-
-  /* Now do the actual startup.  */
-  return start_thread (arg);
-}
-
-
 /* Default thread attributes for the case when the user does not
    provide any.  */
 static const struct pthread_attr default_attr =
@@ -360,7 +388,9 @@ __pthread_create_2_1 (newthread, attr, start_routine, arg)
   pd->arg = arg;
 
   /* Copy the thread attribute flags.  */
-  pd->flags = iattr->flags;
+  struct pthread *self = THREAD_SELF;
+  pd->flags = ((iattr->flags & ~(ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET))
+              | (self->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)));
 
   /* Initialize the field for the ID of the thread which is waiting
      for us.  This is a self-reference in case the thread is created
@@ -368,37 +398,70 @@ __pthread_create_2_1 (newthread, attr, start_routine, arg)
   pd->joinid = iattr->flags & ATTR_FLAG_DETACHSTATE ? pd : NULL;
 
   /* The debug events are inherited from the parent.  */
-  pd->eventbuf = THREAD_SELF->eventbuf;
+  pd->eventbuf = self->eventbuf;
 
 
-  /* Determine scheduling parameters for the thread.
-     XXX How to determine whether scheduling handling is needed?  */
-  if (0 && attr != NULL)
+  /* Copy the parent's scheduling parameters.  The flags will say what
+     is valid and what is not.  */
+  pd->schedpolicy = self->schedpolicy;
+  pd->schedparam = self->schedparam;
+
+  /* Determine scheduling parameters for the thread.  */
+  if (attr != NULL
+      && __builtin_expect ((iattr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0, 0)
+      && (iattr->flags & (ATTR_FLAG_SCHED_SET | ATTR_FLAG_POLICY_SET)) != 0)
     {
-      if (iattr->flags & ATTR_FLAG_NOTINHERITSCHED)
+      INTERNAL_SYSCALL_DECL (err);
+
+      /* Use the scheduling parameters the user provided.  */
+      if (iattr->flags & ATTR_FLAG_POLICY_SET)
+       pd->schedpolicy = iattr->schedpolicy;
+      else if ((pd->flags & ATTR_FLAG_POLICY_SET) == 0)
+       {
+         pd->schedpolicy = INTERNAL_SYSCALL (sched_getscheduler, err, 1, 0);
+         pd->flags |= ATTR_FLAG_POLICY_SET;
+       }
+
+      if (iattr->flags & ATTR_FLAG_SCHED_SET)
+       memcpy (&pd->schedparam, &iattr->schedparam,
+               sizeof (struct sched_param));
+      else if ((pd->flags & ATTR_FLAG_SCHED_SET) == 0)
        {
-         /* Use the scheduling parameters the user provided.  */
-         pd->schedpolicy = iattr->schedpolicy;
-         memcpy (&pd->schedparam, &iattr->schedparam,
-                 sizeof (struct sched_param));
+         INTERNAL_SYSCALL (sched_getparam, err, 2, 0, &pd->schedparam);
+         pd->flags |= ATTR_FLAG_SCHED_SET;
        }
-      else
+
+      /* Check for valid priorities.  */
+      int minprio = INTERNAL_SYSCALL (sched_get_priority_min, err, 1,
+                                     iattr->schedpolicy);
+      int maxprio = INTERNAL_SYSCALL (sched_get_priority_max, err, 1,
+                                     iattr->schedpolicy);
+      if (pd->schedparam.sched_priority < minprio
+         || pd->schedparam.sched_priority > maxprio)
        {
-         /* Just store the scheduling attributes of the parent.  */
-         pd->schedpolicy = __sched_getscheduler (0);
-         __sched_getparam (0, &pd->schedparam);
+         err = EINVAL;
+         goto errout;
        }
     }
 
   /* Pass the descriptor to the caller.  */
   *newthread = (pthread_t) pd;
 
+  /* Remember whether the thread is detached or not.  In case of an
+     error we have to free the stacks of non-detached stillborn
+     threads.  */
+  bool is_detached = IS_DETACHED (pd);
+
   /* Start the thread.  */
-  err = create_thread (pd, STACK_VARIABLES_ARGS);
+  err = create_thread (pd, iattr, STACK_VARIABLES_ARGS);
   if (err != 0)
     {
       /* Something went wrong.  Free the resources.  */
-      __deallocate_stack (pd);
+      if (!is_detached)
+       {
+       errout:
+         __deallocate_stack (pd);
+       }
       return err;
     }
 
@@ -435,6 +498,7 @@ __pthread_create_2_0 (newthread, attr, start_routine, arg)
       new_attr.guardsize = ps;
       new_attr.stackaddr = NULL;
       new_attr.stacksize = 0;
+      new_attr.cpuset = NULL;
 
       /* We will pass this value on to the real implementation.  */
       attr = (pthread_attr_t *) &new_attr;
@@ -445,3 +509,7 @@ __pthread_create_2_0 (newthread, attr, start_routine, arg)
 compat_symbol (libpthread, __pthread_create_2_0, pthread_create,
               GLIBC_2_0);
 #endif
+\f
+/* Information for libthread_db.  */
+
+#include "../nptl_db/db_info.c"