]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - nptl/sysdeps/pthread/createthread.c
Use glibc_likely instead __builtin_expect.
[thirdparty/glibc.git] / nptl / sysdeps / pthread / createthread.c
index 4ee92afec4532b927ec4eb105d627ee4ed5f8ebf..93f93eebd4ab3a21013637da42331530a824f809 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+/* Copyright (C) 2002-2014 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2002.
 
@@ -13,9 +13,8 @@
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #include <sched.h>
 #include <setjmp.h>
 #include <atomic.h>
 #include <ldsodefs.h>
 #include <tls.h>
+#include <stdint.h>
 
+#include "kernel-features.h"
 
-#define CLONE_SIGNAL           (CLONE_SIGHAND | CLONE_THREAD)
+
+#define CLONE_SIGNAL           (CLONE_SIGHAND | CLONE_THREAD)
 
 /* Unless otherwise specified, the thread "register" is going to be
    initialized with a pointer to the TCB.  */
 
 
 #ifndef TLS_MULTIPLE_THREADS_IN_TCB
-/* Variable set to a nonzero value if more than one thread runs or ran.  */
-int __pthread_multiple_threads attribute_hidden;
 /* Pointer to the corresponding variable in libc.  */
 int *__libc_multiple_threads_ptr attribute_hidden;
 #endif
 
 
 static int
-create_thread (struct pthread *pd, STACK_VARIABLES_PARMS)
+do_clone (struct pthread *pd, const struct pthread_attr *attr,
+         int clone_flags, int (*fct) (void *), STACK_VARIABLES_PARMS,
+         int stopped)
 {
 #ifdef PREPARE_CREATE
   PREPARE_CREATE;
 #endif
 
-#ifdef TLS_TCB_AT_TP
-  assert (pd->tcb != NULL);
-#endif
-
-  if (__builtin_expect (THREAD_GETMEM (THREAD_SELF, report_events), 0))
+  if (__glibc_unlikely (stopped != 0))
+    /* We make sure the thread does not run far by forcing it to get a
+       lock.  We lock it here too so that the new thread cannot continue
+       until we tell it to.  */
+    lll_lock (pd->lock, LLL_PRIVATE);
+
+  /* One more thread.  We cannot have the thread do this itself, since it
+     might exist but not have been scheduled yet by the time we've returned
+     and need to check the value to behave correctly.  We must do it before
+     creating the thread, in case it does get scheduled first and then
+     might mistakenly think it was the only thread.  In the failure case,
+     we momentarily store a false value; this doesn't matter because there
+     is no kosher thing a signal handler interrupting us right here can do
+     that cares whether the thread count is correct.  */
+  atomic_increment (&__nptl_nthreads);
+
+  int rc = ARCH_CLONE (fct, STACK_VARIABLES_ARGS, clone_flags,
+                      pd, &pd->tid, TLS_VALUE, &pd->tid);
+
+  if (__glibc_unlikely (rc == -1))
     {
-      /* The parent thread is supposed to report events.  Check whether
-        the TD_CREATE event is needed, too.  */
-      const int _idx = __td_eventword (TD_CREATE);
-      const uint32_t _mask = __td_eventmask (TD_CREATE);
+      atomic_decrement (&__nptl_nthreads); /* Oops, we lied for a second.  */
 
-      if ((_mask & (__nptl_threads_events.event_bits[_idx]
-                   | pd->eventbuf.eventmask.event_bits[_idx])) != 0)
-       {
-         /* We have to report the new thread.  Make sure the thread
-            does not run far by forcing it to get a lock.  We lock it
-            here too so that the new thread cannot continue until we
-            tell it to.  */
-         lll_lock (pd->lock);
-
-         /* Create the thread.  */
-         if (ARCH_CLONE (start_thread_debug, STACK_VARIABLES_ARGS,
-                         CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |
-                         CLONE_SETTLS | CLONE_PARENT_SETTID |
-                         CLONE_CHILD_CLEARTID | CLONE_DETACHED | 0,
-                         pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)
-           /* Failed.  */
-           return errno;
-
-#ifdef TLS_MULTIPLE_THREADS_IN_TCB
-         /* We now have for sure more than one thread.  */
-         pd->multiple_threads = 1;
-#else
-         __pthread_multiple_threads = *__libc_multiple_threads_ptr = 1;
-#endif
+      /* Perhaps a thread wants to change the IDs and if waiting
+        for this stillborn thread.  */
+      if (__builtin_expect (atomic_exchange_acq (&pd->setxid_futex, 0)
+                           == -2, 0))
+       lll_futex_wake (&pd->setxid_futex, 1, LLL_PRIVATE);
 
-         /* Now fill in the information about the new thread in
-            the newly created thread's data structure.  We cannot let
-            the new thread do this since we don't know whether it was
-            already scheduled when we send the event.  */
-         pd->eventbuf.eventnum = TD_CREATE;
-         pd->eventbuf.eventdata = pd;
+      /* Free the resources.  */
+       __deallocate_stack (pd);
 
-         /* Enqueue the descriptor.  */
-         do
-           pd->nextevent = __nptl_last_event;
-         while (atomic_compare_and_exchange_acq (&__nptl_last_event, pd,
-                                                 pd->nextevent) != 0);
+      /* We have to translate error codes.  */
+      return errno == ENOMEM ? EAGAIN : errno;
+    }
 
-         /* Now call the function which signals the event.  */
-         __nptl_create_event ();
+  /* Now we have the possibility to set scheduling parameters etc.  */
+  if (__glibc_unlikely (stopped != 0))
+    {
+      INTERNAL_SYSCALL_DECL (err);
+      int res = 0;
 
-         /* And finally restart the new thread.  */
-         lll_unlock (pd->lock);
+      /* Set the affinity mask if necessary.  */
+      if (attr->cpuset != NULL)
+       {
+         res = INTERNAL_SYSCALL (sched_setaffinity, err, 3, pd->tid,
+                                 attr->cpusetsize, attr->cpuset);
+
+         if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (res, err)))
+           {
+             /* The operation failed.  We have to kill the thread.  First
+                send it the cancellation signal.  */
+             INTERNAL_SYSCALL_DECL (err2);
+           err_out:
+             (void) INTERNAL_SYSCALL (tgkill, err2, 3,
+                                      THREAD_GETMEM (THREAD_SELF, pid),
+                                      pd->tid, SIGCANCEL);
+
+             /* We do not free the stack here because the canceled thread
+                itself will do this.  */
+
+             return (INTERNAL_SYSCALL_ERROR_P (res, err)
+                     ? INTERNAL_SYSCALL_ERRNO (res, err)
+                     : 0);
+           }
+       }
 
-         return 0;
+      /* Set the scheduling parameters.  */
+      if ((attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0)
+       {
+         res = INTERNAL_SYSCALL (sched_setscheduler, err, 3, pd->tid,
+                                 pd->schedpolicy, &pd->schedparam);
+
+         if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (res, err)))
+           goto err_out;
        }
     }
 
-#ifdef NEED_DL_SYSINFO
-  assert (THREAD_GETMEM (THREAD_SELF, sysinfo) == pd->sysinfo);
+  /* We now have for sure more than one thread.  The main thread might
+     not yet have the flag set.  No need to set the global variable
+     again if this is what we use.  */
+  THREAD_SETMEM (THREAD_SELF, header.multiple_threads, 1);
+
+  return 0;
+}
+
+
+static int
+create_thread (struct pthread *pd, const struct pthread_attr *attr,
+              STACK_VARIABLES_PARMS)
+{
+#ifdef TLS_TCB_AT_TP
+  assert (pd->header.tcb != NULL);
 #endif
 
   /* We rely heavily on various flags the CLONE function understands:
@@ -142,23 +176,78 @@ create_thread (struct pthread *pd, STACK_VARIABLES_PARMS)
        sys_exit() in the location pointed to by the seventh parameter
        to CLONE.
 
-     CLONE_DETACHED
-       No signal is generated if the thread exists and it is
-       automatically reaped.
-
      The termination signal is chosen to be zero which means no signal
      is sent.  */
-  if (ARCH_CLONE (start_thread, STACK_VARIABLES_ARGS,
-                 CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |
-                 CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID |
-                 CLONE_DETACHED | 0, pd, &pd->tid, TLS_VALUE, &pd->tid) == -1)
-    /* Failed.  */
-    return errno;
-
-#ifdef TLS_MULTIPLE_THREADS_IN_TCB
-  /* We now have for sure more than one thread.  */
-  THREAD_SETMEM (THREAD_SELF, multiple_threads, 1);
+  int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL
+                    | CLONE_SETTLS | CLONE_PARENT_SETTID
+                    | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM
+                    | 0);
+
+  if (__glibc_unlikely (THREAD_GETMEM (THREAD_SELF, report_events)))
+    {
+      /* The parent thread is supposed to report events.  Check whether
+        the TD_CREATE event is needed, too.  */
+      const int _idx = __td_eventword (TD_CREATE);
+      const uint32_t _mask = __td_eventmask (TD_CREATE);
+
+      if ((_mask & (__nptl_threads_events.event_bits[_idx]
+                   | pd->eventbuf.eventmask.event_bits[_idx])) != 0)
+       {
+         /* We always must have the thread start stopped.  */
+         pd->stopped_start = true;
+
+         /* Create the thread.  We always create the thread stopped
+            so that it does not get far before we tell the debugger.  */
+         int res = do_clone (pd, attr, clone_flags, start_thread,
+                             STACK_VARIABLES_ARGS, 1);
+         if (res == 0)
+           {
+             /* Now fill in the information about the new thread in
+                the newly created thread's data structure.  We cannot let
+                the new thread do this since we don't know whether it was
+                already scheduled when we send the event.  */
+             pd->eventbuf.eventnum = TD_CREATE;
+             pd->eventbuf.eventdata = pd;
+
+             /* Enqueue the descriptor.  */
+             do
+               pd->nextevent = __nptl_last_event;
+             while (atomic_compare_and_exchange_bool_acq (&__nptl_last_event,
+                                                          pd, pd->nextevent)
+                    != 0);
+
+             /* Now call the function which signals the event.  */
+             __nptl_create_event ();
+
+             /* And finally restart the new thread.  */
+             lll_unlock (pd->lock, LLL_PRIVATE);
+           }
+
+         return res;
+       }
+    }
+
+#ifdef NEED_DL_SYSINFO
+  assert (THREAD_SELF_SYSINFO == THREAD_SYSINFO (pd));
 #endif
 
-  return 0;
+  /* Determine whether the newly created threads has to be started
+     stopped since we have to set the scheduling parameters or set the
+     affinity.  */
+  bool stopped = false;
+  if (attr != NULL && (attr->cpuset != NULL
+                      || (attr->flags & ATTR_FLAG_NOTINHERITSCHED) != 0))
+    stopped = true;
+  pd->stopped_start = stopped;
+  pd->parent_cancelhandling = THREAD_GETMEM (THREAD_SELF, cancelhandling);
+
+  /* Actually create the thread.  */
+  int res = do_clone (pd, attr, clone_flags, start_thread,
+                     STACK_VARIABLES_ARGS, stopped);
+
+  if (res == 0 && stopped)
+    /* And finally restart the new thread.  */
+    lll_unlock (pd->lock, LLL_PRIVATE);
+
+  return res;
 }