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); }
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; }
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; }
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; }
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; }
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_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; }
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; }
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; }
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; }
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; }
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; }
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; }