Blame htl/pt-create.c

Packit 6c4009
/* Thread creation.
Packit 6c4009
   Copyright (C) 2000-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library;  if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <pthread.h>
Packit 6c4009
#include <signal.h>
Packit 6c4009
#include <resolv.h>
Packit 6c4009
Packit 6c4009
#include <atomic.h>
Packit 6c4009
#include <hurd/resource.h>
Packit 6c4009
Packit 6c4009
#include <pt-internal.h>
Packit 6c4009
#include <pthreadP.h>
Packit 6c4009
Packit 6c4009
#if IS_IN (libpthread)
Packit 6c4009
# include <ctype.h>
Packit 6c4009
#endif
Packit 6c4009
#ifdef HAVE_USELOCALE
Packit 6c4009
# include <locale.h>
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* The total number of pthreads currently active.  This is defined
Packit 6c4009
   here since it would be really stupid to have a threads-using
Packit 6c4009
   program that doesn't call `pthread_create'.  */
Packit 6c4009
unsigned int __pthread_total;
Packit 6c4009

Packit 6c4009
Packit 6c4009
/* The entry-point for new threads.  */
Packit 6c4009
static void
Packit 6c4009
entry_point (struct __pthread *self, void *(*start_routine) (void *), void *arg)
Packit 6c4009
{
Packit 6c4009
  ___pthread_self = self;
Packit 6c4009
  __resp = &self->res_state;
Packit 6c4009
Packit 6c4009
#if IS_IN (libpthread)
Packit 6c4009
  /* Initialize pointers to locale data.  */
Packit 6c4009
  __ctype_init ();
Packit 6c4009
#endif
Packit 6c4009
#ifdef HAVE_USELOCALE
Packit 6c4009
  /* A fresh thread needs to be bound to the global locale.  */
Packit 6c4009
  uselocale (LC_GLOBAL_LOCALE);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  __pthread_startup ();
Packit 6c4009
Packit 6c4009
  __pthread_exit (start_routine (arg));
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Create a thread with attributes given by ATTR, executing
Packit 6c4009
   START_ROUTINE with argument ARG.  */
Packit 6c4009
int
Packit 6c4009
__pthread_create (pthread_t * thread, const pthread_attr_t * attr,
Packit 6c4009
		  void *(*start_routine) (void *), void *arg)
Packit 6c4009
{
Packit 6c4009
  int err;
Packit 6c4009
  struct __pthread *pthread;
Packit 6c4009
Packit 6c4009
  err = __pthread_create_internal (&pthread, attr, start_routine, arg);
Packit 6c4009
  if (!err)
Packit 6c4009
    *thread = pthread->thread;
Packit 6c4009
  else if (err == ENOMEM)
Packit 6c4009
    err = EAGAIN;
Packit 6c4009
Packit 6c4009
  return err;
Packit 6c4009
}
Packit 6c4009
strong_alias (__pthread_create, pthread_create)
Packit 6c4009
Packit 6c4009
/* Internal version of pthread_create.  See comment in
Packit 6c4009
   pt-internal.h.  */
Packit 6c4009
int
Packit 6c4009
__pthread_create_internal (struct __pthread **thread,
Packit 6c4009
			   const pthread_attr_t * attr,
Packit 6c4009
			   void *(*start_routine) (void *), void *arg)
Packit 6c4009
{
Packit 6c4009
  int err;
Packit 6c4009
  struct __pthread *pthread;
Packit 6c4009
  const struct __pthread_attr *setup;
Packit 6c4009
  sigset_t sigset;
Packit 6c4009
  size_t stacksize;
Packit 6c4009
Packit 6c4009
  /* Allocate a new thread structure.  */
Packit 6c4009
  err = __pthread_alloc (&pthread);
Packit 6c4009
  if (err)
Packit 6c4009
    goto failed;
Packit 6c4009
Packit 6c4009
  /* Use the default attributes if ATTR is NULL.  */
Packit 6c4009
  setup = attr ? attr : &__pthread_default_attr;
Packit 6c4009
Packit 6c4009
  stacksize = setup->__stacksize;
Packit 6c4009
  if (stacksize == 0)
Packit 6c4009
    {
Packit 6c4009
      struct rlimit rlim;
Packit 6c4009
      __getrlimit (RLIMIT_STACK, &rlim);
Packit 6c4009
      if (rlim.rlim_cur != RLIM_INFINITY)
Packit 6c4009
	stacksize = rlim.rlim_cur;
Packit 6c4009
      if (stacksize == 0)
Packit 6c4009
	stacksize = PTHREAD_STACK_DEFAULT;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Initialize the thread state.  */
Packit 6c4009
  pthread->state = (setup->__detachstate == PTHREAD_CREATE_DETACHED
Packit 6c4009
		    ? PTHREAD_DETACHED : PTHREAD_JOINABLE);
Packit 6c4009
Packit 6c4009
  if (setup->__stackaddr)
Packit 6c4009
    {
Packit 6c4009
      pthread->stackaddr = setup->__stackaddr;
Packit 6c4009
Packit 6c4009
      /* If the user supplied a stack, it is not our responsibility to
Packit 6c4009
         setup a stack guard.  */
Packit 6c4009
      pthread->guardsize = 0;
Packit 6c4009
      pthread->stack = 0;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      /* Allocate a stack.  */
Packit 6c4009
      err = __pthread_stack_alloc (&pthread->stackaddr,
Packit 6c4009
				   ((setup->__guardsize + __vm_page_size - 1)
Packit 6c4009
				    / __vm_page_size) * __vm_page_size
Packit 6c4009
				   + stacksize);
Packit 6c4009
      if (err)
Packit 6c4009
	goto failed_stack_alloc;
Packit 6c4009
Packit 6c4009
      pthread->guardsize = setup->__guardsize;
Packit 6c4009
      pthread->stack = 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  pthread->stacksize = stacksize;
Packit 6c4009
Packit 6c4009
  /* Allocate the kernel thread and other required resources.  */
Packit 6c4009
  err = __pthread_thread_alloc (pthread);
Packit 6c4009
  if (err)
Packit 6c4009
    goto failed_thread_alloc;
Packit 6c4009
Packit 6c4009
  pthread->tcb = _dl_allocate_tls (NULL);
Packit 6c4009
  if (pthread->tcb == NULL)
Packit 6c4009
    {
Packit 6c4009
      err = ENOMEM;
Packit 6c4009
      goto failed_thread_tls_alloc;
Packit 6c4009
    }
Packit 6c4009
  pthread->tcb->tcb = pthread->tcb;
Packit 6c4009
Packit 6c4009
  /* And initialize the rest of the machine context.  This may include
Packit 6c4009
     additional machine- and system-specific initializations that
Packit 6c4009
     prove convenient.  */
Packit 6c4009
  err = __pthread_setup (pthread, entry_point, start_routine, arg);
Packit 6c4009
  if (err)
Packit 6c4009
    goto failed_setup;
Packit 6c4009
Packit 6c4009
  /* Initialize the system-specific signal state for the new
Packit 6c4009
     thread.  */
Packit 6c4009
  err = __pthread_sigstate_init (pthread);
Packit 6c4009
  if (err)
Packit 6c4009
    goto failed_sigstate;
Packit 6c4009
Packit 6c4009
  /* If the new thread is joinable, add a reference for the caller.  */
Packit 6c4009
  if (pthread->state == PTHREAD_JOINABLE)
Packit 6c4009
    pthread->nr_refs++;
Packit 6c4009
Packit 6c4009
  /* Set the new thread's signal mask and set the pending signals to
Packit 6c4009
     empty.  POSIX says: "The signal mask shall be inherited from the
Packit 6c4009
     creating thread.  The set of signals pending for the new thread
Packit 6c4009
     shall be empty."  If the currnet thread is not a pthread then we
Packit 6c4009
     just inherit the process' sigmask.  */
Packit 6c4009
  if (__pthread_num_threads == 1)
Packit 6c4009
    err = sigprocmask (0, 0, &sigset);
Packit 6c4009
  else
Packit 6c4009
    err = __pthread_sigstate (_pthread_self (), 0, 0, &sigset, 0);
Packit 6c4009
  assert_perror (err);
Packit 6c4009
Packit 6c4009
  err = __pthread_sigstate (pthread, SIG_SETMASK, &sigset, 0, 1);
Packit 6c4009
  assert_perror (err);
Packit 6c4009
Packit 6c4009
  /* Increase the total number of threads.  We do this before actually
Packit 6c4009
     starting the new thread, since the new thread might immediately
Packit 6c4009
     call `pthread_exit' which decreases the number of threads and
Packit 6c4009
     calls `exit' if the number of threads reaches zero.  Increasing
Packit 6c4009
     the number of threads from within the new thread isn't an option
Packit 6c4009
     since this thread might return and call `pthread_exit' before the
Packit 6c4009
     new thread runs.  */
Packit 6c4009
  atomic_increment (&__pthread_total);
Packit 6c4009
Packit 6c4009
  /* Store a pointer to this thread in the thread ID lookup table.  We
Packit 6c4009
     could use __thread_setid, however, we only lock for reading as no
Packit 6c4009
     other thread should be using this entry (we also assume that the
Packit 6c4009
     store is atomic).  */
Packit 6c4009
  __pthread_rwlock_rdlock (&__pthread_threads_lock);
Packit 6c4009
  __pthread_threads[pthread->thread - 1] = pthread;
Packit 6c4009
  __pthread_rwlock_unlock (&__pthread_threads_lock);
Packit 6c4009
Packit 6c4009
  /* At this point it is possible to guess our pthread ID.  We have to
Packit 6c4009
     make sure that all functions taking a pthread_t argument can
Packit 6c4009
     handle the fact that this thread isn't really running yet.  Since
Packit 6c4009
     the new thread might be passed its ID through pthread_create (to
Packit 6c4009
     avoid calling pthread_self), read it before starting the thread.  */
Packit 6c4009
  *thread = pthread;
Packit 6c4009
Packit 6c4009
  /* Schedule the new thread.  */
Packit 6c4009
  err = __pthread_thread_start (pthread);
Packit 6c4009
  if (err)
Packit 6c4009
    goto failed_starting;
Packit 6c4009
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
Packit 6c4009
failed_starting:
Packit 6c4009
  /* If joinable, a reference was added for the caller.  */
Packit 6c4009
  if (pthread->state == PTHREAD_JOINABLE)
Packit 6c4009
    __pthread_dealloc (pthread);
Packit 6c4009
Packit 6c4009
  __pthread_setid (pthread->thread, NULL);
Packit 6c4009
  atomic_decrement (&__pthread_total);
Packit 6c4009
failed_sigstate:
Packit 6c4009
  __pthread_sigstate_destroy (pthread);
Packit 6c4009
failed_setup:
Packit 6c4009
  _dl_deallocate_tls (pthread->tcb, 1);
Packit 6c4009
  pthread->tcb = NULL;
Packit 6c4009
failed_thread_tls_alloc:
Packit 6c4009
  __pthread_thread_terminate (pthread);
Packit 6c4009
Packit 6c4009
  /* __pthread_thread_terminate has taken care of deallocating the stack and
Packit 6c4009
     the thread structure.  */
Packit 6c4009
  goto failed;
Packit 6c4009
failed_thread_alloc:
Packit 6c4009
  if (pthread->stack)
Packit 6c4009
    __pthread_stack_dealloc (pthread->stackaddr,
Packit 6c4009
			     ((setup->__guardsize + __vm_page_size - 1)
Packit 6c4009
			      / __vm_page_size) * __vm_page_size + stacksize);
Packit 6c4009
failed_stack_alloc:
Packit 6c4009
  __pthread_dealloc (pthread);
Packit 6c4009
failed:
Packit 6c4009
  return err;
Packit 6c4009
}