Fix another potential pthread_join race condition

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@181 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2007-03-30 13:11:19 +00:00
parent f29250c671
commit d2410c93c6
1 changed files with 76 additions and 52 deletions

View File

@ -82,12 +82,13 @@
* *
* Return Value: * Return Value:
* 0 if successful. Otherwise, one of the following error codes: * 0 if successful. Otherwise, one of the following error codes:
* EINVAL The value specified by thread does not refer to a *
* joinable thread. * EINVAL The value specified by thread does not refer to a
* ESRCH No thread could be found corresponding to that * joinable thread.
* specified by the given thread ID. * ESRCH No thread could be found corresponding to that
* EDEADLK A deadlock was detected or the value of thread * specified by the given thread ID.
* specifies the calling thread. * EDEADLK A deadlock was detected or the value of thread
* specifies the calling thread.
* *
* Assumptions: * Assumptions:
* *
@ -144,76 +145,99 @@ int pthread_join(pthread_t thread, pthread_addr_t *pexit_value)
* case, it must be a task and not a pthread. * case, it must be a task and not a pthread.
*/ */
else /* if ((tcb->flags & EDEADLK) == 0) */ else
{ {
ret = EINVAL; ret = EINVAL;
} }
(void)pthread_givesemaphore(&g_join_semaphore); (void)pthread_givesemaphore(&g_join_semaphore);
} }
else if (pjoin->terminated)
{
dbg("Thread has terminated\n");
/* Get the thread exit value from the terminated thread. */
if (pexit_value)
{
dbg("exit_value=0x%p\n", pjoin->exit_value);
*pexit_value = pjoin->exit_value;
}
/* Then remove and deallocate the thread entry. */
(void)pthread_removejoininfo((pid_t)thread);
(void)pthread_givesemaphore(&g_join_semaphore);
sched_free(pjoin);
ret = OK;
}
else else
{ {
dbg("Thread is still running\n"); /* We found the join info structure. Increment for the reference
* to the join structure that we have. This will keep things
/* Relinquish the data set semaphore, making certain that * stable for we have to do
* no task has the opportunity to run between the time
* we relinquish the data set semaphore and the time that
* we wait on the join semaphore.
*/ */
sched_lock(); sched_lock();
pjoin->crefs++; pjoin->crefs++;
(void)pthread_givesemaphore(&g_join_semaphore);
/* Take the thread's join semaphore */ /* Check if the thread is still running. If not, then things are
* simpler. There are still race conditions to be concerned with.
(void)pthread_takesemaphore(&pjoin->exit_sem); * For example, there could be multiple threads executing in the
* 'else' block below when we enter!
/* Get the thread exit value */
if (pexit_value)
{
*pexit_value = pjoin->exit_value;
dbg("exit_value=0x%p\n", pjoin->exit_value);
}
/* Post the thread's join semaphore so that exitting thread
* will know that we have received the data.
*/ */
(void)pthread_givesemaphore(&pjoin->data_sem); if (pjoin->terminated)
{
dbg("Thread has terminated\n");
/* Pre-emption is okay now. */ /* Get the thread exit value from the terminated thread. */
if (pexit_value)
{
dbg("exit_value=0x%p\n", pjoin->exit_value);
*pexit_value = pjoin->exit_value;
}
}
else
{
dbg("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(&g_join_semaphore);
/* 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;
dbg("exit_value=0x%p\n", pjoin->exit_value);
}
/* Post the thread's data semaphore so that the exitting 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(&g_join_semaphore);
}
/* Pre-emption is okay now. The logic still cannot be re-entered
* because we hold the join semaphore
*/
sched_unlock(); sched_unlock();
/* Deallocate the thread entry */ /* Release our reference to the join structure and, if the reference
* count decrements to zero, deallocate the join structure.
*/
(void)pthread_takesemaphore(&g_join_semaphore);
if (--pjoin->crefs <= 0) if (--pjoin->crefs <= 0)
{ {
(void)pthread_destroyjoin(pjoin); (void)pthread_destroyjoin(pjoin);
} }
(void)pthread_givesemaphore(&g_join_semaphore); (void)pthread_givesemaphore(&g_join_semaphore);
ret = OK; ret = OK;
} }