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_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; }
static void *findjoininfo_callback(void *param) { int ret_chk = 0; FAR struct task_group_s *group; FAR struct join_s *st_pjoininfo; g_bpthreadcallback = true; pid_t pid = getpid(); group = task_getgroup(pid); st_pjoininfo = pthread_findjoininfo(group, pid); if (st_pjoininfo == NULL) { printf("pthread_findjoininfo: Fail \n"); g_bpthreadcallback = false; goto err; } ret_chk = pthread_equal((pid_t)st_pjoininfo->thread, pid); if (ret_chk != 1) { printf("tc_pthread_pthread_findjoininfo_destroyjoin pthread_equal fail\n"); g_bpthreadcallback = false; goto err; } pthread_destroyjoin(group, st_pjoininfo); st_pjoininfo = pthread_findjoininfo(group, pid); if (st_pjoininfo != NULL) { printf("pthread_findjoininfo: st_pjoininfo Fail \n"); g_bpthreadcallback = false; goto err; } err: pthread_exit(NULL); return NULL; }
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; }
int pthread_mutex_destroy(FAR pthread_mutex_t *mutex) { struct tcb_s *ptcb; struct join_s *pjoin = NULL; int ret = EINVAL; int status; svdbg("mutex=0x%p\n", mutex); DEBUGASSERT(mutex != NULL); if (mutex != NULL) { /* Make sure the semaphore is stable while we make the following checks */ sched_lock(); /* Is the semaphore available? */ if (mutex->pid >= 0) { DEBUGASSERT(mutex->pid != 0); /* < 0: available, >0 owned, ==0 error */ /* No.. Verify that the PID still exists. We may be destroying * the mutex after cancelling a pthread and the mutex may have * been in a bad state owned by the dead pthread. NOTE: The * following behavior is unspecified for pthread_mutex_destroy() * (see pthread_mutex_consistent()). * * If the holding thread is still valid, then we should be able to * map its PID to the underlying TCB. That is what sched_gettcb() * does. */ ptcb = sched_gettcb(mutex->pid); if (ptcb && ((ptcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD)) { pjoin = pthread_findjoininfo(ptcb->group, ptcb->pid); } /* In some cases, pthread_mutex_destroy can be executed on middle of * exiting pthread which holds mutex. The sched_releasepid() updates * g_pidhash list to NULL by calling _exit() from pthread_exit(). * But, before calling it, pthread_exit() calls pthread_completejoin(). * It gives semaphore of pthread_join before executing _exit(). * It might cause user call pthread_mutex_destroy before updating scheduler. * Checking pjoin structure and terminated element helps to check * whether pthread which holds mutex is exiting or not. */ if (ptcb == NULL || \ (((ptcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_PTHREAD) && (pjoin == NULL || pjoin->terminated == true))) { /* The thread associated with the PID no longer exists */ mutex->pid = -1; /* Reset the semaphore. If threads are were on this * semaphore, then this will awakened them and make * destruction of the semaphore impossible here. */ status = sem_reset((FAR sem_t *)&mutex->sem, 1); if (status < 0) { ret = -status; } /* Check if the reset caused some other thread to lock the * mutex. */ else if (mutex->pid != -1) { /* Yes.. then we cannot destroy the mutex now. */ ret = EBUSY; } /* Destroy the underlying semaphore */ else { status = sem_destroy((FAR sem_t *)&mutex->sem); ret = (status != OK) ? get_errno() : OK; } } else { ret = EBUSY; } } else { /* Destroy the semaphore * * REVISIT: What if there are threads waiting on the semaphore? * Perhaps this logic should all sem_reset() first? */ status = sem_destroy((sem_t *)&mutex->sem); ret = ((status != OK) ? get_errno() : OK); } sched_unlock(); } svdbg("Returning %d\n", ret); return ret; }