Пример #1
0
void pthread_exit(void *retval) {
  /* Get all we need from the tdb before releasing it. */
  nc_thread_descriptor_t    *tdb = nc_get_tdb();
  nc_thread_memory_block_t  *stack_node = tdb->stack_node;
  int32_t                   *is_used = &stack_node->is_used;
  nc_basic_thread_data_t    *basic_data = tdb->basic_data;
  int                       joinable = tdb->joinable;

  /* Call cleanup handlers. */
  while (NULL != __nc_cleanup_handlers) {
    pthread_cleanup_pop(1);
  }

  /* Call the destruction functions for TSD. */
  __nc_tsd_exit();

  __newlib_thread_exit();

  __nc_futex_thread_exit();

  if (__nc_initial_thread_id != basic_data) {
    pthread_mutex_lock(&__nc_thread_management_lock);
    --__nc_running_threads_counter;
    pthread_mutex_unlock(&__nc_thread_management_lock);
  } else {
    /* This is the main thread - wait for other threads to complete. */
    wait_for_threads();
    exit(0);
  }

  pthread_mutex_lock(&__nc_thread_management_lock);

  basic_data->retval = retval;

  if (joinable) {
    /* If somebody is waiting for this thread, signal. */
    basic_data->status = THREAD_TERMINATED;
    pthread_cond_signal(&basic_data->join_condvar);
  }
  /*
   * We can release TLS+TDB - thread id and its return value are still
   * kept in basic_data.
   */
  nc_release_tls_node(tdb->tls_node, tdb);

  if (!joinable) {
    nc_release_basic_data_mu(basic_data);
  }

  /* Now add the stack to the list but keep it marked as used. */
  nc_free_memory_block_mu(THREAD_STACK_MEMORY, stack_node);

  if (1 == __nc_running_threads_counter) {
    pthread_cond_signal(&__nc_last_thread_cond);
  }

  pthread_mutex_unlock(&__nc_thread_management_lock);
  irt_thread.thread_exit(is_used);
  nc_abort();
}
Пример #2
0
static void nc_release_tls_node(nc_thread_memory_block_t *block,
                                nc_thread_descriptor_t *tdb) {
  if (block) {
    if (NULL != tdb->basic_data) {
      tdb->basic_data->tdb = NULL;
    }
    block->is_used = 0;
    nc_free_memory_block_mu(TLS_AND_TDB_MEMORY, block);
  }
}
Пример #3
0
int pthread_create(pthread_t *thread_id,
                   const pthread_attr_t *attr,
                   void *(*start_routine)(void *),
                   void *arg) {
  int retval = EAGAIN;
  void *esp;
  /* Declare the variables outside of the while scope. */
  nc_thread_memory_block_t *stack_node = NULL;
  char *thread_stack = NULL;
  nc_thread_descriptor_t *new_tdb = NULL;
  nc_basic_thread_data_t *new_basic_data = NULL;
  nc_thread_memory_block_t *tls_node = NULL;
  size_t stacksize = PTHREAD_STACK_DEFAULT;
  void *new_tp;

  /* TODO(gregoryd) - right now a single lock is used, try to optimize? */
  pthread_mutex_lock(&__nc_thread_management_lock);

  do {
    /* Allocate the combined TLS + TDB block---see tls.h for explanation. */

    tls_node = nc_allocate_memory_block_mu(TLS_AND_TDB_MEMORY,
                                           __nacl_tls_combined_size(TDB_SIZE));
    if (NULL == tls_node)
      break;

    new_tp = __nacl_tls_initialize_memory(nc_memory_block_to_payload(tls_node),
                                          TDB_SIZE);

    new_tdb = (nc_thread_descriptor_t *)
              ((char *) new_tp + __nacl_tp_tdb_offset(TDB_SIZE));

    /*
     * TODO(gregoryd): consider creating a pool of basic_data structs,
     * similar to stack and TLS+TDB (probably when adding the support for
     * variable stack size).
     */
    new_basic_data = malloc(sizeof(*new_basic_data));
    if (NULL == new_basic_data) {
      /*
       * The tdb should be zero intialized.
       * This just re-emphasizes this requirement.
       */
      new_tdb->basic_data = NULL;
      break;
    }

    nc_tdb_init(new_tdb, new_basic_data);
    new_tdb->tls_node = tls_node;

    /*
     * All the required members of the tdb must be initialized before
     * the thread is started and actually before the global lock is released,
     * since another thread can call pthread_join() or pthread_detach().
     */
    new_tdb->start_func = start_routine;
    new_tdb->state = arg;
    if (attr != NULL) {
      new_tdb->joinable = attr->joinable;
      stacksize = attr->stacksize;
    }

    /* Allocate the stack for the thread. */
    stack_node = nc_allocate_memory_block_mu(THREAD_STACK_MEMORY, stacksize);
    if (NULL == stack_node) {
      retval = EAGAIN;
      break;
    }
    thread_stack = align((uint32_t) nc_memory_block_to_payload(stack_node),
                         kStackAlignment);
    new_tdb->stack_node = stack_node;

    retval = 0;
  } while (0);

  if (0 != retval) {
    pthread_mutex_unlock(&__nc_thread_management_lock);
    goto ret; /* error */
  }

  /*
   * Speculatively increase the thread count.  If thread creation
   * fails, we will decrease it back.  This way the thread count will
   * never be lower than the actual number of threads, but can briefly
   * be higher than that.
   */
  ++__nc_running_threads_counter;

  /*
   * Save the new thread id.  This can not be done after the syscall,
   * because the child thread could have already finished by that
   * time.  If thread creation fails, it will be overriden with -1
   * later.
   */
  *thread_id = new_basic_data;

  pthread_mutex_unlock(&__nc_thread_management_lock);

  /*
   * Calculate the top-of-stack location.  The very first location is a
   * zero address of architecture-dependent width, needed to satisfy the
   * normal ABI alignment requirements for the stack.  (On some machines
   * this is the dummy return address of the thread-start function.)
   *
   * Both thread_stack and stacksize are multiples of 16.
   */
  esp = (void *) (thread_stack + stacksize - kStackPadBelowAlign);
  memset(esp, 0, kStackPadBelowAlign);

  /* Start the thread. */
  retval = irt_thread.thread_create(
      FUN_TO_VOID_PTR(nc_thread_starter), esp, new_tp);
  if (0 != retval) {
    pthread_mutex_lock(&__nc_thread_management_lock);
    /* TODO(gregoryd) : replace with atomic decrement? */
    --__nc_running_threads_counter;
    pthread_mutex_unlock(&__nc_thread_management_lock);
    goto ret;
  }

  assert(0 == retval);

ret:
  if (0 != retval) {
    /* Failed to create a thread. */
    pthread_mutex_lock(&__nc_thread_management_lock);

    nc_release_tls_node(tls_node, new_tdb);
    if (new_basic_data) {
      nc_release_basic_data_mu(new_basic_data);
    }
    if (stack_node) {
      stack_node->is_used = 0;
      nc_free_memory_block_mu(THREAD_STACK_MEMORY, stack_node);
    }

    pthread_mutex_unlock(&__nc_thread_management_lock);
    *thread_id = NACL_PTHREAD_ILLEGAL_THREAD_ID;
  }

  return retval;
}