Beispiel #1
0
int pthread_cond_wait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex)
{
  int ret;

  sdbg("cond=0x%p mutex=0x%p\n", cond, mutex);

  /* Make sure that non-NULL references were provided. */

  if (!cond || !mutex)
    {
      ret = EINVAL;
    }

  /* Make sure that the caller holds the mutex */

  else if (mutex->pid != (int)getpid())
    {
      ret = EPERM;
    }

  else
    {
      /* Give up the mutex */

      sdbg("Give up mutex / take cond\n");

      sched_lock();
      mutex->pid = 0;
      ret = pthread_givesemaphore((sem_t*)&mutex->sem);

      /* Take the semaphore */

      ret |= pthread_takesemaphore((sem_t*)&cond->sem);
      sched_unlock();

      /* Reacquire the mutex */

      sdbg("Reacquire mutex...\n");
      ret |= pthread_takesemaphore((sem_t*)&mutex->sem);
      if (!ret)
        {
          mutex->pid = getpid();;
        }
    }

  sdbg("Returning %d\n", ret);
  return ret;
}
Beispiel #2
0
static void pthread_start(void)
{
  FAR _TCB   *ptcb  = (FAR _TCB*)g_readytorun.head;
  FAR join_t *pjoin = (FAR join_t*)ptcb->joininfo;
  pthread_addr_t exit_status;

  /* Sucessfully spawned, add the pjoin to our data set.
   * Don't re-enable pre-emption until this is done.
   */

  (void)pthread_takesemaphore(&g_join_semaphore);
  pthread_addjoininfo(pjoin);
  (void)pthread_givesemaphore(&g_join_semaphore);

  /* Report to the spawner that we successfully started. */

  pjoin->started = true;
  (void)pthread_givesemaphore(&pjoin->data_sem);

  /* Pass control to the thread entry point.  The argument is
   * argv[1].  argv[0] (the thread name) and argv[2-4] are not made
   * available to the pthread.
   */

  exit_status = (*ptcb->entry.pthread)((pthread_addr_t)ptcb->argv[1]);

  /* The thread has returned */

  pthread_exit(exit_status);
}
Beispiel #3
0
int pthread_completejoin(pid_t pid, FAR void *exit_value)
{
  FAR struct task_group_s *group = task_getgroup(pid);
  FAR struct join_s *pjoin;

  sinfo("pid=%d exit_value=%p group=%p\n", pid, exit_value, group);
  DEBUGASSERT(group);

  /* First, find thread's structure in the private data set. */

  (void)pthread_takesemaphore(&group->tg_joinsem);
  pjoin = pthread_findjoininfo(group, pid);
  if (!pjoin)
    {
      serr("ERROR: Could not find join info, pid=%d\n", pid);
      (void)pthread_givesemaphore(&group->tg_joinsem);
      return ERROR;
    }
  else
    {
      bool waiters;

      /* Save the return exit value in the thread structure. */

      pjoin->terminated = true;
      pjoin->exit_value = exit_value;

      /* Notify waiters of the availability of the exit value */

      waiters = pthread_notifywaiters(pjoin);

      /* If there are no waiters and if the thread is marked as detached.
       * then discard the join information now.  Otherwise, the pthread
       * join logic will call pthread_destroyjoin() when all of the threads
       * have sampled the exit value.
       */

      if (!waiters && pjoin->detached)
        {
           pthread_destroyjoin(group, pjoin);
        }

      /* Giving the following semaphore will allow the waiters
       * to call pthread_destroyjoin.
       */

      (void)pthread_givesemaphore(&group->tg_joinsem);
    }

  return OK;
}
Beispiel #4
0
int pthread_detach(pthread_t thread)
{
  FAR struct tcb_s *rtcb = (FAR struct tcb_s *)g_readytorun.head;
  FAR struct task_group_s *group = rtcb->group;
  FAR struct join_s *pjoin;
  int ret;

  sdbg("Thread=%d group=%p\n", thread, group);
  DEBUGASSERT(group);

  /* Find the entry associated with this pthread. */

  (void)pthread_takesemaphore(&group->tg_joinsem);
  pjoin = pthread_findjoininfo(group, (pid_t)thread);
  if (!pjoin)
    {
      sdbg("Could not find thread entry\n");
      ret = EINVAL;
    }
  else
    {
      /* Has the thread already terminated? */

      if (pjoin->terminated)
        {
          /* YES.. just remove the thread entry. */

          pthread_destroyjoin(group, pjoin);
        }
      else
        {
          /* NO.. Just mark the thread as detached.  It
           * will be removed and deallocated when the
           * thread exits
           */

          pjoin->detached = true;
        }

      /* Either case is successful */

      ret = OK;
    }

  (void)pthread_givesemaphore(&group->tg_joinsem);

  sdbg("Returning %d\n", ret);
  return ret;
}
int pthread_completejoin(pid_t pid, FAR void *exit_value)
{
  FAR join_t *pjoin;

  sdbg("process_id=%d exit_value=%p\n", pid, exit_value);

  /* First, find thread's structure in the private data set. */

  (void)pthread_takesemaphore(&g_join_semaphore);
  pjoin = pthread_findjoininfo(pid);
  if (!pjoin)
    {

      (void)pthread_givesemaphore(&g_join_semaphore);
      return ERROR;
    }
  else
    {
      bool waiters;

      /* Save the return exit value in the thread structure. */

      pjoin->terminated = true;
      pjoin->exit_value = exit_value;

      /* Notify waiters of the availability of the exit value */

      waiters = pthread_notifywaiters(pjoin);

      /* If there are no waiters and if the thread is marked as detached.
       * then discard the join information now.  Otherwise, the pthread
       * join logic will call pthread_destroyjoin() when all of the threads
       * have sampled the exit value.
       */

      if (!waiters && pjoin->detached)
        {
           pthread_destroyjoin(pjoin);
        }

      /* Giving the following semaphore will allow the waiters
       * to call pthread_destroyjoin.
       */

      (void)pthread_givesemaphore(&g_join_semaphore);
    }

  return OK;
}
Beispiel #6
0
static bool pthread_notifywaiters(FAR struct join_s *pjoin)
{
  int ntasks_waiting;
  int status;

  sinfo("pjoin=0x%p\n", pjoin);

  /* Are any tasks waiting for our exit value? */

  status = sem_getvalue(&pjoin->exit_sem, &ntasks_waiting);
  if (status == OK && ntasks_waiting < 0)
    {
      /* Set the data semaphore so that this thread will be
       * awakened when all waiting tasks receive the data
       */

      (void)sem_init(&pjoin->data_sem, 0, (ntasks_waiting+1));

      /* Post the semaphore to restart each thread that is waiting
       * on the semaphore
       */

      do
        {
          status = pthread_givesemaphore(&pjoin->exit_sem);
          if (status == OK)
            {
              status = sem_getvalue(&pjoin->exit_sem, &ntasks_waiting);
            }
        }
      while (ntasks_waiting < 0 && status == OK);

      /* Now wait for all these restarted tasks to obtain the return
       * value.
       */

      (void)pthread_takesemaphore(&pjoin->data_sem);
      return true;
    }

  return false;
}
static void pthread_start(void)
{
  FAR struct pthread_tcb_s *ptcb = (FAR struct pthread_tcb_s *)g_readytorun.head;
  FAR struct task_group_s *group = ptcb->cmn.group;
  FAR struct join_s *pjoin = (FAR struct join_s *)ptcb->joininfo;
  pthread_addr_t exit_status;

  DEBUGASSERT(group && pjoin);

  /* Sucessfully spawned, add the pjoin to our data set. */

  (void)pthread_takesemaphore(&group->tg_joinsem);
  pthread_addjoininfo(group, pjoin);
  (void)pthread_givesemaphore(&group->tg_joinsem);

  /* Report to the spawner that we successfully started. */

  pjoin->started = true;
  (void)pthread_givesemaphore(&pjoin->data_sem);

  /* Pass control to the thread entry point. In the kernel build this has to
   * be handled differently if we are starting a user-space pthread; we have
   * to switch to user-mode before calling into the pthread.
   */

#if defined(CONFIG_BUILD_PROTECTED) || defined(CONFIG_BUILD_KERNEL)
  up_pthread_start(ptcb->cmn.entry.pthread, ptcb->arg);
  exit_status = NULL;
#else
  exit_status = (*ptcb->cmn.entry.pthread)(ptcb->arg);
#endif

  /* The thread has returned (should never happen in the kernel mode case) */

  pthread_exit(exit_status);
}
int pthread_cond_timedwait(FAR pthread_cond_t *cond, FAR pthread_mutex_t *mutex,
                           FAR const struct timespec *abstime)
{
  FAR struct tcb_s *rtcb = this_task();
  int ticks;
  int mypid = (int)getpid();
  irqstate_t int_state;
  int ret = OK;
  int status;

  sdbg("cond=0x%p mutex=0x%p abstime=0x%p\n", cond, mutex, abstime);

  DEBUGASSERT(rtcb->waitdog == NULL);

  /* Make sure that non-NULL references were provided. */

  if (!cond || !mutex)
    {
      ret = EINVAL;
    }

  /* Make sure that the caller holds the mutex */

  else if (mutex->pid != mypid)
    {
      ret = EPERM;
    }

  /* If no wait time is provided, this function degenerates to
   * the same behavior as pthread_cond_wait().
   */

  else if (!abstime)
    {
      ret = pthread_cond_wait(cond, mutex);
    }

  else
    {
      /* Create a watchdog */

      rtcb->waitdog = wd_create();
      if (!rtcb->waitdog)
        {
          ret = EINVAL;
        }
      else
        {
          sdbg("Give up mutex...\n");

          /* We must disable pre-emption and interrupts here so that
           * the time stays valid until the wait begins.   This adds
           * complexity because we assure that interrupts and
           * pre-emption are re-enabled correctly.
           */

          sched_lock();
          int_state = irqsave();

          /* Convert the timespec to clock ticks.  We must disable pre-emption
           * here so that this time stays valid until the wait begins.
           */

          ret = clock_abstime2ticks(CLOCK_REALTIME, abstime, &ticks);
          if (ret)
            {
              /* Restore interrupts  (pre-emption will be enabled when
               * we fall through the if/then/else
               */

              irqrestore(int_state);
            }
          else
            {
              /* Check the absolute time to wait.  If it is now or in the past, then
               * just return with the timedout condition.
               */

              if (ticks <= 0)
                {
                  /* Restore interrupts and indicate that we have already timed out.
                   * (pre-emption will be enabled when we fall through the
                   * if/then/else
                   */

                  irqrestore(int_state);
                  ret = ETIMEDOUT;
                }
              else
                {
                  /* Give up the mutex */

                  mutex->pid = -1;
                  ret = pthread_givesemaphore((FAR sem_t *)&mutex->sem);
                  if (ret)
                    {
                      /* Restore interrupts  (pre-emption will be enabled when
                       * we fall through the if/then/else)
                       */

                      irqrestore(int_state);
                    }
                  else
                    {
                      /* Start the watchdog */

                      wd_start(rtcb->waitdog, ticks, (wdentry_t)pthread_condtimedout,
                               2, (uint32_t)mypid, (uint32_t)SIGCONDTIMEDOUT);

                      /* Take the condition semaphore.  Do not restore interrupts
                       * until we return from the wait.  This is necessary to
                       * make sure that the watchdog timer and the condition wait
                       * are started atomically.
                       */

                      status = sem_wait((FAR sem_t *)&cond->sem);

                      /* Did we get the condition semaphore. */

                      if (status != OK)
                        {
                          /* NO.. Handle the special case where the semaphore wait was
                           * awakened by the receipt of a signal -- presumably the
                           * signal posted by pthread_condtimedout().
                           */

                          if (get_errno() == EINTR)
                            {
                              sdbg("Timedout!\n");
                              ret = ETIMEDOUT;
                            }
                          else
                            {
                              ret = EINVAL;
                            }
                        }

                      /* The interrupts stay disabled until after we sample the errno.
                       * This is because when debug is enabled and the console is used
                       * for debug output, then the errno can be altered by interrupt
                       * handling! (bad)
                       */

                      irqrestore(int_state);
                    }

                  /* Reacquire the mutex (retaining the ret). */

                  sdbg("Re-locking...\n");
                  status = pthread_takesemaphore((FAR sem_t *)&mutex->sem);
                  if (!status)
                    {
                      mutex->pid = mypid;
                    }
                  else if (!ret)
                    {
                      ret = status;
                    }
                }

              /* Re-enable pre-emption (It is expected that interrupts
               * have already been re-enabled in the above logic)
               */

              sched_unlock();
            }

          /* We no longer need the watchdog */

          wd_delete(rtcb->waitdog);
          rtcb->waitdog = NULL;
        }
    }

  sdbg("Returning %d\n", ret);
  return ret;
}
Beispiel #9
0
int pthread_join(pthread_t thread, FAR pthread_addr_t *pexit_value)
{
  FAR struct tcb_s *rtcb = this_task();
  FAR struct task_group_s *group = rtcb->group;
  FAR struct join_s *pjoin;
  int ret;

  sdbg("thread=%d group=%p\n", thread, group);
  DEBUGASSERT(group);

  /* First make sure that this is not an attempt to join to
   * ourself.
   */

  if ((pid_t)thread == getpid())
    {
      return EDEADLK;
    }

  /* Make sure no other task is mucking with the data structures
   * while we are performing the following operations.  NOTE:
   * we can be also sure that pthread_exit() will not execute
   * because it will also attempt to get this semaphore.
   */

  (void)pthread_takesemaphore(&group->tg_joinsem);

  /* Find the join information associated with this thread.
   * This can fail for one of three reasons:  (1) There is no
   * thread associated with 'thread,' (2) the thread is a task
   * and does not have join information, or (3) the thread
   * was detached and has exited.
   */

  pjoin = pthread_findjoininfo(group, (pid_t)thread);
  if (!pjoin)
    {
      /* Determine what kind of error to return */

      FAR struct tcb_s *tcb = sched_gettcb((pthread_t)thread);

      sdbg("Could not find thread data\n");

      /* Case (1) or (3) -- we can't tell which.  Assume (3) */

      if (!tcb)
        {
          ret = ESRCH;
        }

      /* The thread is still active but has no join info.  In that
       * case, it must be a task and not a pthread.
       */

      else
        {
          ret = EINVAL;
        }

      (void)pthread_givesemaphore(&group->tg_joinsem);
    }
  else
    {
      /* We found the join info structure.  Increment for the reference
       * to the join structure that we have.  This will keep things
       * stable for we have to do
       */

      sched_lock();
      pjoin->crefs++;

      /* Check if the thread is still running.  If not, then things are
       * simpler.  There are still race conditions to be concerned with.
       * For example, there could be multiple threads executing in the
       * 'else' block below when we enter!
       */

      if (pjoin->terminated)
        {
          sdbg("Thread has terminated\n");

          /* Get the thread exit value from the terminated thread. */

          if (pexit_value)
            {
              sdbg("exit_value=0x%p\n", pjoin->exit_value);
              *pexit_value = pjoin->exit_value;
            }
        }
      else
        {
          sdbg("Thread is still running\n");

          /* Relinquish the data set semaphore.  Since pre-emption is
           * disabled, we can be certain that no task has the
           * opportunity to run between the time we relinquish the
           * join semaphore and the time that we wait on the thread exit
           * semaphore.
           */

          (void)pthread_givesemaphore(&group->tg_joinsem);

          /* Take the thread's thread exit semaphore.  We will sleep here
           * until the thread exits.  We need to exercise caution because
           * there could be multiple threads waiting here for the same
           * pthread to exit.
           */

          (void)pthread_takesemaphore(&pjoin->exit_sem);

          /* The thread has exited! Get the thread exit value */

          if (pexit_value)
            {
             *pexit_value = pjoin->exit_value;
              sdbg("exit_value=0x%p\n", pjoin->exit_value);
            }

          /* Post the thread's data semaphore so that the exiting thread
           * will know that we have received the data.
           */

          (void)pthread_givesemaphore(&pjoin->data_sem);

          /* Retake the join semaphore, we need to hold this when
           * pthread_destroyjoin is called.
           */

          (void)pthread_takesemaphore(&group->tg_joinsem);
        }

      /* Pre-emption is okay now. The logic still cannot be re-entered
       * because we hold the join semaphore
       */

      sched_unlock();

      /* Release our reference to the join structure and, if the reference
       * count decrements to zero, deallocate the join structure.
       */

      if (--pjoin->crefs <= 0)
        {
          (void)pthread_destroyjoin(group, pjoin);
        }

      (void)pthread_givesemaphore(&group->tg_joinsem);
      ret = OK;
    }

  sdbg("Returning %d\n", ret);
  return ret;
}
Beispiel #10
0
int pthread_create(FAR pthread_t *thread, FAR pthread_attr_t *attr,
                   pthread_startroutine_t start_routine, pthread_addr_t arg)
{
  FAR _TCB *ptcb;
  FAR join_t *pjoin;
  int status;
  int priority;
#if CONFIG_RR_INTERVAL > 0
  int policy;
#endif
  pid_t pid;

  /* If attributes were not supplied, use the default attributes */

  if (!attr)
    {
      attr = &g_default_pthread_attr;
    }

  /* Allocate a TCB for the new task. */

  ptcb = (FAR _TCB*)kzalloc(sizeof(_TCB));
  if (!ptcb)
    {
      return ENOMEM;
    }

  /* Associate file descriptors with the new task */

  status = sched_setuppthreadfiles(ptcb);
  if (status != OK)
    {
      sched_releasetcb(ptcb);
      return status;
    }

  /* Share the parent's envionment */

  (void)env_share(ptcb);

  /* Allocate a detachable structure to support pthread_join logic */

  pjoin = (FAR join_t*)kzalloc(sizeof(join_t));
  if (!pjoin)
    {
      sched_releasetcb(ptcb);
      return ENOMEM;
    }

  /* Allocate the stack for the TCB */

  status = up_create_stack(ptcb, attr->stacksize);
  if (status != OK)
    {
      sched_releasetcb(ptcb);
      sched_free(pjoin);
      return ENOMEM;
    }

  /* Should we use the priority and scheduler specified in the
   * pthread attributes?  Or should we use the current thread's
   * priority and scheduler?
   */

  if (attr->inheritsched == PTHREAD_INHERIT_SCHED)
    {
      /* Get the priority for this thread. */

      struct sched_param param;
      status = sched_getparam(0, &param);
      if (status == OK)
        {
          priority = param.sched_priority;
        }
      else
        {
          priority = SCHED_FIFO;
        }

      /* Get the scheduler policy for this thread */

#if CONFIG_RR_INTERVAL > 0
      policy = sched_getscheduler(0);
      if (policy == ERROR)
        {
          policy = SCHED_FIFO;
        }
#endif
    }
  else
    {
      /* Use the priority and scheduler from the attributes */

      priority = attr->priority;
#if CONFIG_RR_INTERVAL > 0
      policy   = attr->policy;
#endif
    }

  /* Mark this task as a pthread (this setting will be needed in
   * task_schedsetup() when up_initial_state() is called.
   */

  ptcb->flags |= TCB_FLAG_TTYPE_PTHREAD;

  /* Initialize the task control block */

  status  = task_schedsetup(ptcb, priority, pthread_start,
                            (main_t)start_routine);
  if (status != OK)
    {

      sched_releasetcb(ptcb);
      sched_free(pjoin);
      return EBUSY;
    }

  /* Configure the TCB for a pthread receiving on parameter
   * passed by value
   */

  pthread_argsetup(ptcb, arg);

  /* Attach the join info to the TCB. */

  ptcb->joininfo = (void*)pjoin;

  /* If round robin scheduling is selected, set the appropriate flag
   * in the TCB.
   */

#if CONFIG_RR_INTERVAL > 0
  if (policy == SCHED_RR)
    {
      ptcb->flags    |= TCB_FLAG_ROUND_ROBIN;
      ptcb->timeslice = CONFIG_RR_INTERVAL / MSEC_PER_TICK;
    }
#endif

  /* Get the assigned pid before we start the task (who knows what
   * could happen to ptcb after this!).  Copy this ID into the join structure
   * as well.
   */

  pid = (int)ptcb->pid;
  pjoin->thread = (pthread_t)pid;

  /* Initialize the semaphores in the join structure to zero. */

  status = sem_init(&pjoin->data_sem, 0, 0);
  if (status == OK)
    {
      status = sem_init(&pjoin->exit_sem, 0, 0);
    }

  /* Activate the task */

  sched_lock();
  if (status == OK)
    {
      status = task_activate(ptcb);
    }

  if (status == OK)
    {
      /* Wait for the task to actually get running and to register
       * its join_t
       */

      (void)pthread_takesemaphore(&pjoin->data_sem);

      /* Return the thread information to the caller */

      if (thread) *thread = (pthread_t)pid;
      if (!pjoin->started) status = ERROR;

      sched_unlock();
      (void)sem_destroy(&pjoin->data_sem);
    }
  else
    {
      sched_unlock();
      dq_rem((FAR dq_entry_t*)ptcb, (dq_queue_t*)&g_inactivetasks);
      (void)sem_destroy(&pjoin->data_sem);
      (void)sem_destroy(&pjoin->exit_sem);
      sched_releasetcb(ptcb);
      sched_free(pjoin);
      return EIO;
    }

  return OK;
}
int pthread_create(FAR pthread_t *thread, FAR const pthread_attr_t *attr,
                   pthread_startroutine_t start_routine, pthread_addr_t arg)
{
  FAR struct pthread_tcb_s *ptcb;
  FAR struct join_s *pjoin;
  struct sched_param param;
  int policy;
  int errcode;
  pid_t pid;
  int ret;
#ifdef HAVE_TASK_GROUP
  bool group_joined = false;
#endif

  /* If attributes were not supplied, use the default attributes */

  if (!attr)
    {
      attr = &g_default_pthread_attr;
    }

  /* Allocate a TCB for the new task. */

  ptcb = (FAR struct pthread_tcb_s *)kmm_zalloc(sizeof(struct pthread_tcb_s));
  if (!ptcb)
    {
      sdbg("ERROR: Failed to allocate TCB\n");
      return ENOMEM;
    }

#ifdef HAVE_TASK_GROUP
  /* Bind the parent's group to the new TCB (we have not yet joined the
   * group).
   */

  ret = group_bind(ptcb);
  if (ret < 0)
    {
      errcode = ENOMEM;
      goto errout_with_tcb;
    }
#endif

#ifdef CONFIG_ARCH_ADDRENV
  /* Share the address environment of the parent task group. */

  ret = up_addrenv_attach(ptcb->cmn.group,
                          (FAR struct tcb_s *)g_readytorun.head);
  if (ret < 0)
    {
      errcode = -ret;
      goto errout_with_tcb;
    }
#endif

  /* Allocate a detachable structure to support pthread_join logic */

  pjoin = (FAR struct join_s *)kmm_zalloc(sizeof(struct join_s));
  if (!pjoin)
    {
      sdbg("ERROR: Failed to allocate join\n");
      errcode = ENOMEM;
      goto errout_with_tcb;
    }

  /* Allocate the stack for the TCB */

  ret = up_create_stack((FAR struct tcb_s *)ptcb, attr->stacksize,
                        TCB_FLAG_TTYPE_PTHREAD);
  if (ret != OK)
    {
      errcode = ENOMEM;
      goto errout_with_join;
    }

  /* Should we use the priority and scheduler specified in the pthread
   * attributes?  Or should we use the current thread's priority and
   * scheduler?
   */

  if (attr->inheritsched == PTHREAD_INHERIT_SCHED)
    {
      /* Get the priority (and any other scheduling parameters) for this
       * thread.
       */

      ret = sched_getparam(0, &param);
      if (ret == ERROR)
        {
          errcode = get_errno();
          goto errout_with_join;
        }

      /* Get the scheduler policy for this thread */

      policy = sched_getscheduler(0);
      if (policy == ERROR)
        {
          errcode = get_errno();
          goto errout_with_join;
        }
    }
  else
    {
      /* Use the scheduler policy and policy the attributes */

      policy                             = attr->policy;
      param.sched_priority               = attr->priority;

#ifdef CONFIG_SCHED_SPORADIC
      param.sched_ss_low_priority        = attr->low_priority;
      param.sched_ss_max_repl            = attr->max_repl;
      param.sched_ss_repl_period.tv_sec  = attr->repl_period.tv_sec;
      param.sched_ss_repl_period.tv_nsec = attr->repl_period.tv_nsec;
      param.sched_ss_init_budget.tv_sec  = attr->budget.tv_sec;
      param.sched_ss_init_budget.tv_nsec = attr->budget.tv_nsec;
#endif
    }

#ifdef CONFIG_SCHED_SPORADIC
  if (policy == SCHED_SPORADIC)
    {
      FAR struct sporadic_s *sporadic;
      int repl_ticks;
      int budget_ticks;

      /* Convert timespec values to system clock ticks */

      (void)clock_time2ticks(&param.sched_ss_repl_period, &repl_ticks);
      (void)clock_time2ticks(&param.sched_ss_init_budget, &budget_ticks);

      /* The replenishment period must be greater than or equal to the
       * budget period.
       */

      if (repl_ticks < budget_ticks)
        {
          errcode = EINVAL;
          goto errout_with_join;
        }

      /* Initialize the sporadic policy */

      ret = sched_sporadic_initialize(&ptcb->cmn);
      if (ret >= 0)
        {
          sporadic               = ptcb->cmn.sporadic;
          DEBUGASSERT(sporadic != NULL);

          /* Save the sporadic scheduling parameters */

          sporadic->hi_priority  = param.sched_priority;
          sporadic->low_priority = param.sched_ss_low_priority;
          sporadic->max_repl     = param.sched_ss_max_repl;
          sporadic->repl_period  = repl_ticks;
          sporadic->budget       = budget_ticks;

          /* And start the first replenishment interval */

          ret = sched_sporadic_start(&ptcb->cmn);
        }

      /* Handle any failures */

      if (ret < 0)
        {
          errcode = -ret;
          goto errout_with_join;
        }
    }
#endif

  /* Initialize the task control block */

  ret = pthread_schedsetup(ptcb, param.sched_priority, pthread_start,
                           start_routine);
  if (ret != OK)
    {
      errcode = EBUSY;
      goto errout_with_join;
    }

  /* Configure the TCB for a pthread receiving on parameter
   * passed by value
   */

  pthread_argsetup(ptcb, arg);

#ifdef HAVE_TASK_GROUP
  /* Join the parent's task group */

  ret = group_join(ptcb);
  if (ret < 0)
    {
      errcode = ENOMEM;
      goto errout_with_join;
    }

  group_joined = true;
#endif

  /* Attach the join info to the TCB. */

  ptcb->joininfo = (FAR void *)pjoin;

  /* Set the appropriate scheduling policy in the TCB */

  ptcb->cmn.flags &= ~TCB_FLAG_POLICY_MASK;
  switch (policy)
    {
      default:
        DEBUGPANIC();
      case SCHED_FIFO:
        ptcb->cmn.flags    |= TCB_FLAG_SCHED_FIFO;
        break;

#if CONFIG_RR_INTERVAL > 0
      case SCHED_RR:
        ptcb->cmn.flags    |= TCB_FLAG_SCHED_RR;
        ptcb->cmn.timeslice = MSEC2TICK(CONFIG_RR_INTERVAL);
        break;
#endif

#ifdef CONFIG_SCHED_SPORADIC
      case SCHED_SPORADIC:
        ptcb->cmn.flags    |= TCB_FLAG_SCHED_SPORADIC;
        break;
#endif

#if 0 /* Not supported */
      case SCHED_OTHER:
        ptcb->cmn.flags    |= TCB_FLAG_SCHED_OTHER;
        break;
#endif
    }

  /* Get the assigned pid before we start the task (who knows what
   * could happen to ptcb after this!).  Copy this ID into the join structure
   * as well.
   */

  pid = (int)ptcb->cmn.pid;
  pjoin->thread = (pthread_t)pid;

  /* Initialize the semaphores in the join structure to zero. */

  ret = sem_init(&pjoin->data_sem, 0, 0);
  if (ret == OK)
    {
      ret = sem_init(&pjoin->exit_sem, 0, 0);
    }

  /* Activate the task */

  sched_lock();
  if (ret == OK)
    {
      ret = task_activate((FAR struct tcb_s *)ptcb);
    }

  if (ret == OK)
    {
      /* Wait for the task to actually get running and to register
       * its join structure.
       */

      (void)pthread_takesemaphore(&pjoin->data_sem);

      /* Return the thread information to the caller */

      if (thread)
       {
         *thread = (pthread_t)pid;
       }

      if (!pjoin->started)
        {
          ret = EINVAL;
        }

      sched_unlock();
      (void)sem_destroy(&pjoin->data_sem);
    }
  else
    {
      sched_unlock();
      dq_rem((FAR dq_entry_t *)ptcb, (FAR dq_queue_t *)&g_inactivetasks);
      (void)sem_destroy(&pjoin->data_sem);
      (void)sem_destroy(&pjoin->exit_sem);

      errcode = EIO;
      goto errout_with_join;
    }

  return ret;

errout_with_join:
  sched_kfree(pjoin);
  ptcb->joininfo = NULL;

errout_with_tcb:
#ifdef HAVE_TASK_GROUP
  /* Clear group binding */

  if (ptcb && !group_joined)
    {
      ptcb->cmn.group = NULL;
    }
#endif

  sched_releasetcb((FAR struct tcb_s *)ptcb, TCB_FLAG_TTYPE_PTHREAD);
  return errcode;
}