diff options
Diffstat (limited to 'libpthread/nptl/sysdeps/pthread/createthread.c')
| -rw-r--r-- | libpthread/nptl/sysdeps/pthread/createthread.c | 255 | 
1 files changed, 255 insertions, 0 deletions
| diff --git a/libpthread/nptl/sysdeps/pthread/createthread.c b/libpthread/nptl/sysdeps/pthread/createthread.c new file mode 100644 index 000000000..88ffe09d3 --- /dev/null +++ b/libpthread/nptl/sysdeps/pthread/createthread.c @@ -0,0 +1,255 @@ +/* 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. + +   The GNU C Library is free software; you can redistribute it and/or +   modify it under the terms of the GNU Lesser General Public +   License as published by the Free Software Foundation; either +   version 2.1 of the License, or (at your option) any later version. + +   The GNU C Library is distributed in the hope that it will be useful, +   but WITHOUT ANY WARRANTY; without even the implied warranty of +   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +   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.  */ + +#include <sched.h> +#include <setjmp.h> +#include <signal.h> +#include <stdlib.h> +#include <atomic.h> +#include <ldsodefs.h> +#include <tls.h> + +#include <bits/kernel-features.h> + + +#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_VALUE +# define TLS_VALUE pd +#endif + +#ifndef ARCH_CLONE +# define ARCH_CLONE __clone +#endif + + +#ifndef TLS_MULTIPLE_THREADS_IN_TCB +/* Pointer to the corresponding variable in libc.  */ +int *__libc_multiple_threads_ptr attribute_hidden; +#endif + + +static int +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 + +  if (stopped) +    /* 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); + +  /* 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); + +  if (ARCH_CLONE (fct, STACK_VARIABLES_ARGS, clone_flags, +		  pd, &pd->tid, TLS_VALUE, &pd->tid) == -1) +    { +      atomic_decrement (&__nptl_nthreads); /* Oops, we lied for a second.  */ + +      /* Failed.  If the thread is detached, remove the TCB here since +	 the caller cannot do this.  The caller remembered the thread +	 as detached and cannot reverify that it is not since it must +	 not access the thread descriptor again.  */ +      if (IS_DETACHED (pd)) +	__deallocate_stack (pd); + +      return errno; +    } + +  /* Now we have the possibility to set scheduling parameters etc.  */ +  if (__builtin_expect (stopped != 0, 0)) +    { +      INTERNAL_SYSCALL_DECL (err); +      int res = 0; + +      /* Set the affinity mask if necessary.  */ +      if (attr->cpuset != NULL) +	{ +	  res = INTERNAL_SYSCALL (sched_setaffinity, err, 3, pd->tid, +				  sizeof (cpu_set_t), attr->cpuset); + +	  if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0)) +	    { +	      /* The operation failed.  We have to kill the thread.  First +		 send it the cancellation signal.  */ +	      INTERNAL_SYSCALL_DECL (err2); +	    err_out: +#if __ASSUME_TGKILL +	      (void) INTERNAL_SYSCALL (tgkill, err2, 3, +				       THREAD_GETMEM (THREAD_SELF, pid), +				       pd->tid, SIGCANCEL); +#else +	      (void) INTERNAL_SYSCALL (tkill, err2, 2, pd->tid, SIGCANCEL); +#endif + +	      return (INTERNAL_SYSCALL_ERROR_P (res, err) +		      ? INTERNAL_SYSCALL_ERRNO (res, err) +		      : 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 (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (res, err), 0)) +	    goto err_out; +	} +    } + +  /* 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: + +     CLONE_VM, CLONE_FS, CLONE_FILES +	These flags select semantics with shared address space and +	file descriptors according to what POSIX requires. + +     CLONE_SIGNAL +	This flag selects the POSIX signal semantics. + +     CLONE_SETTLS +	The sixth parameter to CLONE determines the TLS area for the +	new thread. + +     CLONE_PARENT_SETTID +	The kernels writes the thread ID of the newly created thread +	into the location pointed to by the fifth parameters to CLONE. + +	Note that it would be semantically equivalent to use +	CLONE_CHILD_SETTID but it is be more expensive in the kernel. + +     CLONE_CHILD_CLEARTID +	The kernels clears the thread ID of a thread that has called +	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.  */ +  int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL +		     | CLONE_SETTLS | CLONE_PARENT_SETTID +		     | CLONE_CHILD_CLEARTID | CLONE_SYSVSEM +#if __ASSUME_NO_CLONE_DETACHED == 0 +		     | CLONE_DETACHED +#endif +		     | 0); + +  if (__builtin_expect (THREAD_GETMEM (THREAD_SELF, report_events), 0)) +    { +      /* 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); +	    } + +	  return res; +	} +    } + +#ifdef NEED_DL_SYSINFO +  assert (THREAD_SELF_SYSINFO == THREAD_SYSINFO (pd)); +#endif + +  /* 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; + +  /* 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); + +  return res; +} | 
