Пример #1
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);
}
Пример #2
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;
}
Пример #3
0
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;
}
Пример #4
0
int pthread_cond_broadcast(FAR pthread_cond_t *cond)
{
  int ret = OK;
  int sval;

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

  if (!cond)
    {
      ret = EINVAL;
    }
  else
    {
      /* Disable pre-emption until all of the waiting threads have been
       * restarted. This is necessary to assure that the sval behaves as
       * expected in the following while loop
       */

      sched_lock();

      /* Get the current value of the semaphore */

      if (sem_getvalue((sem_t*)&cond->sem, &sval) != OK)
        {
          ret = EINVAL;
        }
      else
        {
          /* Loop until all of the waiting threads have been restarted. */

          while (sval < 0)
            {
              /* If the value is less than zero (meaning that one or more
               * thread is waiting), then post the condition semaphore.
               * Only the highest priority waiting thread will get to execute
               */

              ret = pthread_givesemaphore((sem_t*)&cond->sem);

              /* Increment the semaphore count (as was done by the
               * above post).
               */

              sval++;
            }
        }

      /* Now we can let the restarted threads run */

      sched_unlock();
    }

  sdbg("Returning %d\n", ret);
  return ret;
}
Пример #5
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;
}
Пример #6
0
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);
}
Пример #7
0
int pthread_cond_signal(FAR pthread_cond_t *cond)
{
  int ret = OK;
  int sval;

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

  if (!cond)
    {
      ret = EINVAL;
    }
  else
    {
      /* Get the current value of the semaphore */

      if (sem_getvalue((FAR sem_t *)&cond->sem, &sval) != OK)
        {
          ret = EINVAL;
        }

      /* If the value is less than zero (meaning that one or more
       * thread is waiting), then post the condition semaphore.
       * Only the highest priority waiting thread will get to execute
       */

      else
        {
          /* One of my objectives in this design was to make pthread_cond_signal
           * usable from interrupt handlers.  However, from interrupt handlers,
           * you cannot take the associated mutex before signaling the condition.
           * As a result, I think that there could be a race condition with
           * the following logic which assumes that the if sval < 0 then the
           * thread is waiting.  Without the mutex, there is no atomic, protected
           * operation that will guarantee this to be so.
           */

          sdbg("sval=%d\n", sval);
          if (sval < 0)
            {
              sdbg("Signalling...\n");
              ret = pthread_givesemaphore((FAR sem_t *)&cond->sem);
            }
        }
    }

  sdbg("Returning %d\n", ret);
  return ret;
}
Пример #8
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;
}
Пример #9
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;
}
Пример #10
0
int pthread_cond_signal(FAR pthread_cond_t *cond)
{
  int ret = OK;
  int sval;

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

  if (!cond)
    {
      ret = EINVAL;
    }
  else
    {
      /* Get the current value of the semaphore */

      if (sem_getvalue((sem_t*)&cond->sem, &sval) != OK)
        {
          ret = EINVAL;
        }

      /* If the value is less than zero (meaning that one or more
       * thread is waiting), then post the condition semaphore.
       * Only the highest priority waiting thread will get to execute
       */

      else
        {
          sdbg("sval=%d\n", sval);
          if (sval < 0)
            {
              sdbg("Signalling...\n");
              ret = pthread_givesemaphore((sem_t*)&cond->sem);
            }
        }
    }

  sdbg("Returning %d\n", ret);
  return ret;
}
Пример #11
0
int pthread_mutex_unlock(FAR pthread_mutex_t *mutex)
{
  int ret = OK;

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

  if (!mutex)
    {
      ret = EINVAL;
    }
  else
    {
      /* Make sure the semaphore is stable while we make the following
       * checks.  This all needs to be one atomic action.
       */

      sched_lock();

      /* Does the calling thread own the semaphore? */

      if (mutex->pid != (int)getpid())
        {
          /* No... return an error (default behavior is like PTHREAD_MUTEX_ERRORCHECK) */

          sdbg("Holder=%d returning EPERM\n", mutex->pid);
          ret = EPERM;
        }


      /* Yes, the caller owns the semaphore.. Is this a recursive mutex? */

#ifdef CONFIG_MUTEX_TYPES
      else if (mutex->type == PTHREAD_MUTEX_RECURSIVE && mutex->nlocks > 1)
        {
          /* This is a recursive mutex and we there are multiple locks held. Retain
           * the mutex lock, just decrement the count of locks held, and return
           * success.
           */
          mutex->nlocks--;
        }
#endif

      /* This is either a non-recursive mutex or is the outermost unlock of
       * a recursive mutex.
       */

      else
        {
          /* Nullify the pid and lock count then post the semaphore */

          mutex->pid    = -1;
#ifdef CONFIG_MUTEX_TYPES
          mutex->nlocks = 0;
#endif
          ret = pthread_givesemaphore((sem_t*)&mutex->sem);
        }
      sched_unlock();
    }

  sdbg("Returning %d\n", ret);
  return ret;
}
Пример #12
0
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;
}
Пример #13
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;
}