From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 21428 invoked by alias); 19 Sep 2002 16:17:44 -0000 Mailing-List: contact pthreads-win32-help@sources.redhat.com; run by ezmlm Precedence: bulk List-Subscribe: List-Archive: List-Post: List-Help: , Sender: pthreads-win32-owner@sources.redhat.com Received: (qmail 21421 invoked from network); 19 Sep 2002 16:17:40 -0000 Received: from unknown (HELO ptldme-mls2.maine.rr.com) (24.93.159.133) by sources.redhat.com with SMTP; 19 Sep 2002 16:17:40 -0000 Received: from maine.rr.com (ptd-24-194-195-52.maine.rr.com [24.194.195.52]) by ptldme-mls2.maine.rr.com (8.11.0/RoadRunner 1.03) with ESMTP id g8JGHbf04056 for ; Thu, 19 Sep 2002 12:17:37 -0400 (EDT) Message-ID: <3D89F90A.2030805@maine.rr.com> Date: Thu, 19 Sep 2002 09:17:00 -0000 From: Michael Johnson User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.0; en-US; rv:1.1) Gecko/20020826 X-Accept-Language: en-us, en MIME-Version: 1.0 To: pthreads-win32 discussion list Subject: Deadlock interaction between pthread_cond_check_need_init.c and pthread_cond_destroy.c Content-Type: text/plain; charset=ISO-8859-1; format=flowed Content-Transfer-Encoding: 7bit X-SW-Source: 2002/txt/msg00097.txt.bz2 When two different threads exist, and one is attempting to destroy a condition variable while the other is attempting to initialize a condition variable that was created with PTHREAD_COND_INITIALIZER, a deadlock can occur: Thread A Thread B Enters ptw32_cond_check_need_init Enters pthread_cond_destroy EnterCriticalSection(&ptw32_cond_test_init_lock) (now holds ptw32_cond_test_init_lock) EnterCriticalSection(&ptw32_cond_list_lock) Enters pthread_cond_init Determines that condvar is static initialized EnterCriticalSection(&ptw32_cond_list_lock) EnterCriticalSection(&ptw32_cond_test_init_lock) (now waiting to enter ptw32_cond_list_lock) (now waiting to enter ptw32_cond_test_init_lock) deadlocked It appears from reading the code and from a patch that I made that the following change to pthread_cond_destroy appears to fix this: | | |{| | pthread_cond_t cv;| | int result = 0, result1 = 0, result2 = 0;| || | /*| | * Assuming any race condition here is harmless.| | */| | if (cond == NULL| | || *cond == NULL)| | {| | return EINVAL;| | }| || | if (*cond != PTHREAD_COND_INITIALIZER)| | {| | EnterCriticalSection(&ptw32_cond_list_lock);| || | cv = *cond;| || | /*| | * Close the gate; this will synchronize this thread with| | * all already signaled waiters to let them retract their| | * waiter status - SEE NOTE 1 ABOVE!!!| | */| | if (sem_wait(&(cv->semBlockLock)) != 0)| | {| | return errno;| | }| || | /*| | * !TRY! lock mtxUnblockLock; try will detect busy condition| | * and will not cause a deadlock with respect to concurrent| | * signal/broadcast.| | */| | if ((result = pthread_mutex_trylock(&(cv->mtxUnblockLock))) != 0)| | {| | (void) sem_post(&(cv->semBlockLock));| | return result;| | }| || | /*| | * Check whether cv is still busy (still has waiters)| | */| | if (cv->nWaitersBlocked > cv->nWaitersGone)| | {| | if (sem_post(&(cv->semBlockLock)) != 0)| | {| | result = errno;| | }| | result1 = pthread_mutex_unlock(&(cv->mtxUnblockLock));| | result2 = EBUSY;| | }| | else| | {| | /*| | * Now it is safe to destroy| | */| | *cond = NULL;| || | if (sem_destroy(&(cv->semBlockLock)) != 0)| | {| | result = errno;| | }| | if (sem_destroy(&(cv->semBlockQueue)) != 0)| | {| | result1 = errno;| | }| | if ((result2 = pthread_mutex_unlock(&(cv->mtxUnblockLock))) == 0)| | {| | result2 = pthread_mutex_destroy(&(cv->mtxUnblockLock));| | }| || | /* Unlink the CV from the list */| || | if (ptw32_cond_list_head == cv)| | {| | ptw32_cond_list_head = cv->next;| | }| | else| | {| | cv->prev->next = cv->next;| | }| || | if (ptw32_cond_list_tail == cv)| | {| | ptw32_cond_list_tail = cv->prev;| | }| | else| | {| | cv->next->prev = cv->prev;| | }| || | (void) free(cv);| | }| || | LeaveCriticalSection(&ptw32_cond_list_lock);| | }| | else| | {| | /*| | * See notes in ptw32_cond_check_need_init() above also.| | */| | EnterCriticalSection(&ptw32_cond_test_init_lock);| || | /*| | * Check again.| | */| | if (*cond == PTHREAD_COND_INITIALIZER)| | {| | /*| | * This is all we need to do to destroy a statically| | * initialised cond that has not yet been used (initialised).| | * If we get to here, another thread waiting to initialise| | * this cond will get an EINVAL. That's OK.| | */| | *cond = NULL;| | }| | else| | {| | /*| | * The cv has been initialised while we were waiting| | * so assume it's in use.| | */| | result = EBUSY;| | }| || | LeaveCriticalSection(&ptw32_cond_test_init_lock);| | }| || | return ((result != 0) ? result : ((result1 != 0) ? result1 : result2));| |}|