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
+4 -25
View File
@@ -23,10 +23,8 @@ struct ConditionVariable;
struct ConditionVariableEntry struct ConditionVariableEntry
: DoublyLinkedListLinkImpl<ConditionVariableEntry> { : DoublyLinkedListLinkImpl<ConditionVariableEntry> {
public: public:
#if KDEBUG ConditionVariableEntry();
inline ConditionVariableEntry(); ~ConditionVariableEntry();
inline ~ConditionVariableEntry();
#endif
bool Add(const void* object); bool Add(const void* object);
status_t Wait(uint32 flags = 0, bigtime_t timeout = 0); status_t Wait(uint32 flags = 0, bigtime_t timeout = 0);
@@ -38,7 +36,8 @@ public:
inline ConditionVariable* Variable() const { return fVariable; } inline ConditionVariable* Variable() const { return fVariable; }
private: private:
inline void AddToLockedVariable(ConditionVariable* variable); inline void _AddToLockedVariable(ConditionVariable* variable);
void _RemoveFromVariable();
private: private:
spinlock fLock; spinlock fLock;
@@ -100,26 +99,6 @@ protected:
}; };
#if KDEBUG
inline
ConditionVariableEntry::ConditionVariableEntry()
: fVariable(NULL)
{
}
inline
ConditionVariableEntry::~ConditionVariableEntry()
{
if (fVariable != NULL) {
panic("Destroying condition variable entry %p, but it's still "
"attached to variable %p\n", this, fVariable);
}
}
#endif
inline void inline void
ConditionVariable::NotifyOne(status_t result) ConditionVariable::NotifyOne(status_t result)
{ {
+43 -24
View File
@@ -90,6 +90,19 @@ dump_condition_variable(int argc, char** argv)
// #pragma mark - ConditionVariableEntry // #pragma mark - ConditionVariableEntry
ConditionVariableEntry::ConditionVariableEntry()
: fVariable(NULL)
{
}
ConditionVariableEntry::~ConditionVariableEntry()
{
if (fVariable != NULL)
_RemoveFromVariable();
}
bool bool
ConditionVariableEntry::Add(const void* object) ConditionVariableEntry::Add(const void* object)
{ {
@@ -108,14 +121,14 @@ ConditionVariableEntry::Add(const void* object)
SpinLocker variableLocker(variable->fLock); SpinLocker variableLocker(variable->fLock);
hashLocker.Unlock(); hashLocker.Unlock();
AddToLockedVariable(variable); _AddToLockedVariable(variable);
return true; return true;
} }
inline void inline void
ConditionVariableEntry::AddToLockedVariable(ConditionVariable* variable) ConditionVariableEntry::_AddToLockedVariable(ConditionVariable* variable)
{ {
ASSERT(fVariable == NULL); 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 status_t
ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout) ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
{ {
@@ -157,27 +196,7 @@ ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
else else
error = thread_block(); error = thread_block();
entryLocker.Lock(); _RemoveFromVariable();
// 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;
}
return error; return error;
} }
@@ -266,7 +285,7 @@ void
ConditionVariable::Add(ConditionVariableEntry* entry) ConditionVariable::Add(ConditionVariableEntry* entry)
{ {
InterruptsSpinLocker _(fLock); InterruptsSpinLocker _(fLock);
entry->AddToLockedVariable(this); entry->_AddToLockedVariable(this);
} }
@@ -53,6 +53,19 @@ static mutex sConditionVariablesLock = MUTEX_INITIALIZER("condition variables");
// #pragma mark - ConditionVariableEntry // #pragma mark - ConditionVariableEntry
ConditionVariableEntry::ConditionVariableEntry()
: fVariable(NULL)
{
}
ConditionVariableEntry::~ConditionVariableEntry()
{
if (fVariable != NULL)
_RemoveFromVariable();
}
bool bool
ConditionVariableEntry::Add(const void* object) ConditionVariableEntry::Add(const void* object)
{ {
@@ -95,14 +108,7 @@ ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
while ((error = _kern_block_thread(flags, timeout)) == B_INTERRUPTED) { while ((error = _kern_block_thread(flags, timeout)) == B_INTERRUPTED) {
} }
conditionLocker.Lock(); _RemoveFromVariable();
// remove entry from variable, if not done yet
if (fVariable != NULL) {
fVariable->fEntries.Remove(this);
fVariable = NULL;
}
return error; return error;
} }
@@ -118,7 +124,7 @@ ConditionVariableEntry::Wait(const void* object, uint32 flags,
inline void inline void
ConditionVariableEntry::AddToLockedVariable(ConditionVariable* variable) ConditionVariableEntry::_AddToLockedVariable(ConditionVariable* variable)
{ {
fThread = get_current_thread(); fThread = get_current_thread();
fVariable = variable; fVariable = variable;
@@ -127,6 +133,17 @@ ConditionVariableEntry::AddToLockedVariable(ConditionVariable* variable)
} }
void
ConditionVariableEntry::_RemoveFromVariable()
{
MutexLocker _(sConditionVariablesLock);
if (fVariable != NULL) {
fVariable->fEntries.Remove(this);
fVariable = NULL;
}
}
// #pragma mark - ConditionVariable // #pragma mark - ConditionVariable
@@ -178,7 +195,7 @@ void
ConditionVariable::Add(ConditionVariableEntry* entry) ConditionVariable::Add(ConditionVariableEntry* entry)
{ {
MutexLocker _(sConditionVariablesLock); MutexLocker _(sConditionVariablesLock);
entry->AddToLockedVariable(this); entry->_AddToLockedVariable(this);
} }