kernel: Rework ConditionVariableEntry destruction.

It is no longer an error to destroy a ConditionVariableEntry
that is still attached to a ConditionVariable; it will
now be implicitly detached in that case.

This makes ConditionVariableEntrys much eaiser to use
from an API standpoint.

Change-Id: I03c676d3a198aa885de733d3e1729b15f80de031
Reviewed-on: https://review.haiku-os.org/c/haiku/+/2301
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Augustin Cavalier
2020-03-07 21:27:05 +00:00
committed by waddlesplash
parent 1c9c772967
commit 1728b8c777
3 changed files with 74 additions and 59 deletions
+43 -24
View File
@@ -90,6 +90,19 @@ dump_condition_variable(int argc, char** argv)
// #pragma mark - ConditionVariableEntry
ConditionVariableEntry::ConditionVariableEntry()
: fVariable(NULL)
{
}
ConditionVariableEntry::~ConditionVariableEntry()
{
if (fVariable != NULL)
_RemoveFromVariable();
}
bool
ConditionVariableEntry::Add(const void* object)
{
@@ -108,14 +121,14 @@ ConditionVariableEntry::Add(const void* object)
SpinLocker variableLocker(variable->fLock);
hashLocker.Unlock();
AddToLockedVariable(variable);
_AddToLockedVariable(variable);
return true;
}
inline void
ConditionVariableEntry::AddToLockedVariable(ConditionVariable* variable)
ConditionVariableEntry::_AddToLockedVariable(ConditionVariable* variable)
{
ASSERT(fVariable == NULL);
@@ -127,6 +140,32 @@ ConditionVariableEntry::AddToLockedVariable(ConditionVariable* variable)
}
void
ConditionVariableEntry::_RemoveFromVariable()
{
InterruptsLocker _;
SpinLocker entryLocker(fLock);
if (fVariable != NULL) {
SpinLocker conditionLocker(fVariable->fLock);
if (fVariable->fEntries.Contains(this)) {
fVariable->fEntries.Remove(this);
} else {
entryLocker.Unlock();
// The variable's fEntries did not contain us, but we currently
// have the variable's lock acquired. This must mean we are in
// a race with the variable's Notify. It is possible we will be
// destroyed immediately upon returning here, so we need to
// spin until our fVariable member is unset by the Notify thread
// and then re-acquire our own lock to avoid a use-after-free.
while (atomic_pointer_get(&fVariable) != NULL) {}
entryLocker.Lock();
}
fVariable = NULL;
}
}
status_t
ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
{
@@ -157,27 +196,7 @@ ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
else
error = thread_block();
entryLocker.Lock();
// Remove entry from variable, if not done yet.
if (fVariable != NULL) {
SpinLocker conditionLocker(fVariable->fLock);
if (fVariable->fEntries.Contains(this)) {
fVariable->fEntries.Remove(this);
} else {
entryLocker.Unlock();
// The variable's fEntries did not contain us, but we currently
// have the variable's lock acquired. This must mean we are in
// a race with the variable's Notify. It is possible we will be
// destroyed immediately upon returning here, so we need to
// spin until our fVariable member is unset by the Notify thread
// and then re-acquire our own lock to avoid a use-after-free.
while (atomic_pointer_get(&fVariable) != NULL) {}
entryLocker.Lock();
}
fVariable = NULL;
}
_RemoveFromVariable();
return error;
}
@@ -266,7 +285,7 @@ void
ConditionVariable::Add(ConditionVariableEntry* entry)
{
InterruptsSpinLocker _(fLock);
entry->AddToLockedVariable(this);
entry->_AddToLockedVariable(this);
}