Blame htl/pt-alloc.c

Packit 6c4009
/* Allocate a new thread structure.
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 <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
Packit 6c4009
#include <pt-internal.h>
Packit 6c4009
Packit 6c4009
/* This braindamage is necessary because the standard says that some
Packit 6c4009
   of the threads functions "shall fail" if "No thread could be found
Packit 6c4009
   corresponding to that specified by the given thread ID."  */
Packit 6c4009
Packit 6c4009
/* Thread ID lookup table.  */
Packit 6c4009
struct __pthread **__pthread_threads;
Packit 6c4009
Packit 6c4009
/* The size of the thread ID lookup table.  */
Packit 6c4009
int __pthread_max_threads;
Packit 6c4009
Packit 6c4009
/* The total number of thread IDs currently in use, or on the list of
Packit 6c4009
   available thread IDs.  */
Packit 6c4009
int __pthread_num_threads;
Packit 6c4009
Packit 6c4009
/* A lock for the table, and the other variables above.  */
Packit 6c4009
pthread_rwlock_t __pthread_threads_lock;
Packit 6c4009
Packit 6c4009
/* List of thread structures corresponding to free thread IDs.  */
Packit 6c4009
struct __pthread *__pthread_free_threads;
Packit 6c4009
pthread_mutex_t __pthread_free_threads_lock;
Packit 6c4009
Packit 6c4009
static inline error_t
Packit 6c4009
initialize_pthread (struct __pthread *new)
Packit 6c4009
{
Packit 6c4009
  error_t err;
Packit 6c4009
Packit 6c4009
  err = __pthread_init_specific (new);
Packit 6c4009
  if (err)
Packit 6c4009
    return err;
Packit 6c4009
Packit 6c4009
  new->nr_refs = 1;
Packit 6c4009
  new->cancel_lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
Packit 6c4009
  new->cancel_hook = NULL;
Packit 6c4009
  new->cancel_hook_arg = NULL;
Packit 6c4009
  new->cancel_state = PTHREAD_CANCEL_ENABLE;
Packit 6c4009
  new->cancel_type = PTHREAD_CANCEL_DEFERRED;
Packit 6c4009
  new->cancel_pending = 0;
Packit 6c4009
Packit 6c4009
  new->state_lock = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
Packit 6c4009
  new->state_cond = (pthread_cond_t) PTHREAD_COND_INITIALIZER;
Packit 6c4009
Packit 6c4009
  new->cancelation_handlers = 0;
Packit 6c4009
Packit 6c4009
  memset (&new->res_state, '\0', sizeof (new->res_state));
Packit 6c4009
Packit 6c4009
  new->tcb = NULL;
Packit 6c4009
Packit 6c4009
  new->next = 0;
Packit 6c4009
  new->prevp = 0;
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Allocate a new thread structure and its pthread thread ID (but not
Packit 6c4009
   a kernel thread).  */
Packit 6c4009
int
Packit 6c4009
__pthread_alloc (struct __pthread **pthread)
Packit 6c4009
{
Packit 6c4009
  error_t err;
Packit 6c4009
Packit 6c4009
  struct __pthread *new;
Packit 6c4009
  struct __pthread **threads;
Packit 6c4009
  struct __pthread **old_threads;
Packit 6c4009
  int max_threads;
Packit 6c4009
  int new_max_threads;
Packit 6c4009
Packit 6c4009
  __pthread_mutex_lock (&__pthread_free_threads_lock);
Packit 6c4009
  for (new = __pthread_free_threads; new; new = new->next)
Packit 6c4009
    {
Packit 6c4009
      /* There is no need to take NEW->STATE_LOCK: if NEW is on this
Packit 6c4009
         list, then it is protected by __PTHREAD_FREE_THREADS_LOCK
Packit 6c4009
         except in __pthread_dealloc where after it is added to the
Packit 6c4009
         list (with the lock held), it drops the lock and then sets
Packit 6c4009
         NEW->STATE and immediately stops using NEW.  */
Packit 6c4009
      if (new->state == PTHREAD_TERMINATED)
Packit 6c4009
	{
Packit 6c4009
	  __pthread_dequeue (new);
Packit 6c4009
	  break;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  __pthread_mutex_unlock (&__pthread_free_threads_lock);
Packit 6c4009
Packit 6c4009
  if (new)
Packit 6c4009
    {
Packit 6c4009
      if (new->tcb)
Packit 6c4009
	{
Packit 6c4009
	  /* Drop old values */
Packit 6c4009
	  _dl_deallocate_tls (new->tcb, 1);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      err = initialize_pthread (new);
Packit 6c4009
      if (!err)
Packit 6c4009
	*pthread = new;
Packit 6c4009
      return err;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Allocate a new thread structure.  */
Packit 6c4009
  new = malloc (sizeof (struct __pthread));
Packit 6c4009
  if (new == NULL)
Packit 6c4009
    return ENOMEM;
Packit 6c4009
Packit 6c4009
  err = initialize_pthread (new);
Packit 6c4009
  if (err)
Packit 6c4009
    {
Packit 6c4009
      free (new);
Packit 6c4009
      return err;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
retry:
Packit 6c4009
  __pthread_rwlock_wrlock (&__pthread_threads_lock);
Packit 6c4009
Packit 6c4009
  if (__pthread_num_threads < __pthread_max_threads)
Packit 6c4009
    {
Packit 6c4009
      /* We have a free slot.  Use the slot number plus one as the
Packit 6c4009
         thread ID for the new thread.  */
Packit 6c4009
      new->thread = 1 + __pthread_num_threads++;
Packit 6c4009
      __pthread_threads[new->thread - 1] = NULL;
Packit 6c4009
Packit 6c4009
      __pthread_rwlock_unlock (&__pthread_threads_lock);
Packit 6c4009
Packit 6c4009
      *pthread = new;
Packit 6c4009
      return 0;
Packit 6c4009
    }
Packit 6c4009
#ifdef PTHREAD_THREADS_MAX
Packit 6c4009
  else if (__pthread_num_threads >= PTHREAD_THREADS_MAX)
Packit 6c4009
    {
Packit 6c4009
      /* We have reached the limit on the number of threads per process.  */
Packit 6c4009
      __pthread_rwlock_unlock (&__pthread_threads_lock);
Packit 6c4009
Packit 6c4009
      free (new);
Packit 6c4009
      return EAGAIN;
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* We are going to enlarge the threads table.  Save its current
Packit 6c4009
     size.  We're going to release the lock before doing the necessary
Packit 6c4009
     memory allocation, since that's a potentially blocking operation.  */
Packit 6c4009
  max_threads = __pthread_max_threads;
Packit 6c4009
Packit 6c4009
  __pthread_rwlock_unlock (&__pthread_threads_lock);
Packit 6c4009
Packit 6c4009
  /* Allocate a new lookup table that's twice as large.  */
Packit 6c4009
  new_max_threads
Packit 6c4009
      = max_threads > 0 ? max_threads * 2 : _POSIX_THREAD_THREADS_MAX;
Packit 6c4009
  threads = malloc (new_max_threads * sizeof (struct __pthread *));
Packit 6c4009
  if (threads == NULL)
Packit 6c4009
    {
Packit 6c4009
      free (new);
Packit 6c4009
      return ENOMEM;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __pthread_rwlock_wrlock (&__pthread_threads_lock);
Packit 6c4009
Packit 6c4009
  /* Check if nobody else has already enlarged the table.  */
Packit 6c4009
  if (max_threads != __pthread_max_threads)
Packit 6c4009
    {
Packit 6c4009
      /* Yep, they did.  */
Packit 6c4009
      __pthread_rwlock_unlock (&__pthread_threads_lock);
Packit 6c4009
Packit 6c4009
      /* Free the newly allocated table and try again to allocate a slot.  */
Packit 6c4009
      free (threads);
Packit 6c4009
      goto retry;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Copy over the contents of the old table.  */
Packit 6c4009
  memcpy (threads, __pthread_threads,
Packit 6c4009
	  __pthread_max_threads * sizeof (struct __pthread *));
Packit 6c4009
Packit 6c4009
  /* Save the location of the old table.  We want to deallocate its
Packit 6c4009
     storage after we released the lock.  */
Packit 6c4009
  old_threads = __pthread_threads;
Packit 6c4009
Packit 6c4009
  /* Replace the table with the new one.  */
Packit 6c4009
  __pthread_max_threads = new_max_threads;
Packit 6c4009
  __pthread_threads = threads;
Packit 6c4009
Packit 6c4009
  /* And allocate ourselves one of the newly created slots.  */
Packit 6c4009
  new->thread = 1 + __pthread_num_threads++;
Packit 6c4009
  __pthread_threads[new->thread - 1] = NULL;
Packit 6c4009
Packit 6c4009
  __pthread_rwlock_unlock (&__pthread_threads_lock);
Packit 6c4009
Packit 6c4009
  free (old_threads);
Packit 6c4009
Packit 6c4009
  *pthread = new;
Packit 6c4009
  return 0;
Packit 6c4009
}