Addressed a deadlock race condition: Acquiration of condition variable
and thread spinlock was reverse in Wait() and Notify(). The thread lock is now the outer lock -- this way it is still possible to call Notify() with the thread lock being held. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22401 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -82,8 +82,7 @@ protected:
|
|||||||
void Notify(bool all, bool threadsLocked);
|
void Notify(bool all, bool threadsLocked);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void _Notify(bool all, bool threadsLocked,
|
void _Notify(bool all, status_t result);
|
||||||
status_t result);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
const void* fObject;
|
const void* fObject;
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ PrivateConditionVariableEntry::Wait(uint32 flags)
|
|||||||
}
|
}
|
||||||
|
|
||||||
InterruptsLocker _;
|
InterruptsLocker _;
|
||||||
|
SpinLocker threadLocker(thread_spinlock);
|
||||||
SpinLocker locker(sConditionVariablesLock);
|
SpinLocker locker(sConditionVariablesLock);
|
||||||
|
|
||||||
// get first entry for this thread
|
// get first entry for this thread
|
||||||
@@ -159,10 +160,9 @@ PrivateConditionVariableEntry::Wait(uint32 flags)
|
|||||||
thread->condition_variable_entry = firstEntry;
|
thread->condition_variable_entry = firstEntry;
|
||||||
thread->sem.blocking = -1;
|
thread->sem.blocking = -1;
|
||||||
|
|
||||||
GRAB_THREAD_LOCK();
|
|
||||||
locker.Unlock();
|
locker.Unlock();
|
||||||
scheduler_reschedule();
|
scheduler_reschedule();
|
||||||
RELEASE_THREAD_LOCK();
|
threadLocker.Unlock();
|
||||||
|
|
||||||
return firstEntry->fResult;
|
return firstEntry->fResult;
|
||||||
}
|
}
|
||||||
@@ -289,6 +289,7 @@ PrivateConditionVariable::Unpublish(bool threadsLocked)
|
|||||||
ASSERT(fObject != NULL);
|
ASSERT(fObject != NULL);
|
||||||
|
|
||||||
InterruptsLocker _;
|
InterruptsLocker _;
|
||||||
|
SpinLocker threadLocker(threadsLocked ? NULL : &thread_spinlock);
|
||||||
SpinLocker locker(sConditionVariablesLock);
|
SpinLocker locker(sConditionVariablesLock);
|
||||||
|
|
||||||
#if KDEBUG
|
#if KDEBUG
|
||||||
@@ -304,7 +305,7 @@ PrivateConditionVariable::Unpublish(bool threadsLocked)
|
|||||||
fObjectType = NULL;
|
fObjectType = NULL;
|
||||||
|
|
||||||
if (fEntries)
|
if (fEntries)
|
||||||
_Notify(true, threadsLocked, B_ENTRY_NOT_FOUND);
|
_Notify(true, B_ENTRY_NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -314,6 +315,7 @@ PrivateConditionVariable::Notify(bool all, bool threadsLocked)
|
|||||||
ASSERT(fObject != NULL);
|
ASSERT(fObject != NULL);
|
||||||
|
|
||||||
InterruptsLocker _;
|
InterruptsLocker _;
|
||||||
|
SpinLocker threadLocker(threadsLocked ? NULL : &thread_spinlock);
|
||||||
SpinLocker locker(sConditionVariablesLock);
|
SpinLocker locker(sConditionVariablesLock);
|
||||||
|
|
||||||
#if KDEBUG
|
#if KDEBUG
|
||||||
@@ -325,18 +327,17 @@ PrivateConditionVariable::Notify(bool all, bool threadsLocked)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (fEntries)
|
if (fEntries)
|
||||||
_Notify(all, threadsLocked, B_OK);
|
_Notify(all, B_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//! Called with interrupts disabled and the condition variable spinlock held.
|
/*! Called with interrupts disabled and the condition variable spinlock and
|
||||||
|
thread lock held.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
PrivateConditionVariable::_Notify(bool all, bool threadsLocked, status_t result)
|
PrivateConditionVariable::_Notify(bool all, status_t result)
|
||||||
{
|
{
|
||||||
// dequeue and wake up the blocked threads
|
// dequeue and wake up the blocked threads
|
||||||
if (!threadsLocked)
|
|
||||||
GRAB_THREAD_LOCK();
|
|
||||||
|
|
||||||
while (PrivateConditionVariableEntry* entry = fEntries) {
|
while (PrivateConditionVariableEntry* entry = fEntries) {
|
||||||
fEntries = entry->fVariableNext;
|
fEntries = entry->fVariableNext;
|
||||||
entry->fVariableNext = NULL;
|
entry->fVariableNext = NULL;
|
||||||
@@ -370,36 +371,18 @@ PrivateConditionVariable::_Notify(bool all, bool threadsLocked, status_t result)
|
|||||||
if (!all)
|
if (!all)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!threadsLocked)
|
|
||||||
RELEASE_THREAD_LOCK();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark -
|
||||||
|
|
||||||
|
|
||||||
/*! Interrupts must be disabled, thread lock must be held. Note, that the thread
|
/*! Interrupts must be disabled, thread lock must be held.
|
||||||
lock may temporarily be released.
|
|
||||||
*/
|
*/
|
||||||
status_t
|
status_t
|
||||||
condition_variable_interrupt_thread(struct thread* thread)
|
condition_variable_interrupt_thread(struct thread* thread)
|
||||||
{
|
{
|
||||||
if (thread == NULL || thread->state != B_THREAD_WAITING
|
|
||||||
|| thread->condition_variable_entry == NULL) {
|
|
||||||
return B_BAD_VALUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_id threadID = thread->id;
|
|
||||||
|
|
||||||
// We also need the condition variable spin lock, so, in order to respect
|
|
||||||
// the locking order, we must temporarily release the thread lock.
|
|
||||||
RELEASE_THREAD_LOCK();
|
|
||||||
SpinLocker locker(sConditionVariablesLock);
|
SpinLocker locker(sConditionVariablesLock);
|
||||||
GRAB_THREAD_LOCK();
|
|
||||||
|
|
||||||
// re-get the thread and do the checks again
|
|
||||||
thread = thread_get_thread_struct_locked(threadID);
|
|
||||||
|
|
||||||
if (thread == NULL || thread->state != B_THREAD_WAITING
|
if (thread == NULL || thread->state != B_THREAD_WAITING
|
||||||
|| thread->condition_variable_entry == NULL) {
|
|| thread->condition_variable_entry == NULL) {
|
||||||
|
|||||||
@@ -274,8 +274,7 @@ is_signal_blocked(int signal)
|
|||||||
|
|
||||||
|
|
||||||
/*! Tries to interrupt a thread waiting for a semaphore or a condition variable.
|
/*! Tries to interrupt a thread waiting for a semaphore or a condition variable.
|
||||||
Interrupts must be disabled, the thread lock be held. Note, that the thread
|
Interrupts must be disabled, the thread lock be held.
|
||||||
lock may temporarily be released.
|
|
||||||
*/
|
*/
|
||||||
static status_t
|
static status_t
|
||||||
signal_interrupt_thread(struct thread* thread)
|
signal_interrupt_thread(struct thread* thread)
|
||||||
|
|||||||
Reference in New Issue
Block a user