[+] AuUtility::ADestructionWatcher::DoUnderLock

This commit is contained in:
Reece Wilson 2024-02-10 05:41:19 +00:00
parent 2b0ed79729
commit c1ccb81b7b

View File

@ -22,6 +22,7 @@ namespace Aurora::Utility
friend struct ADestructionWatcher; friend struct ADestructionWatcher;
Threading::Waitables::FutexWaitable mutex; Threading::Waitables::FutexWaitable mutex;
Threading::Waitables::FutexWaitable dtorSignal;
ADestructionWatcher *pFirstWatcher {}; ADestructionWatcher *pFirstWatcher {};
AuList<ADestructionWatcher *> listpWatches; AuList<ADestructionWatcher *> listpWatches;
@ -35,6 +36,7 @@ namespace Aurora::Utility
inline ~ADestructionWatcher(); inline ~ADestructionWatcher();
inline void Observe(DestructionWatch *pWatch); inline void Observe(DestructionWatch *pWatch);
inline void Observe(DestructionWatch &pWatch);
inline bool IsObservedAlive(); inline bool IsObservedAlive();
inline bool IsObservedDead(); inline bool IsObservedDead();
@ -46,12 +48,49 @@ namespace Aurora::Utility
private: private:
friend struct DestructionWatch; friend struct DestructionWatch;
friend class Threading::LockGuard<ADestructionWatcher *>;
inline void OnSelfDTOR(); inline void OnSelfDTOR();
inline void OnDTOR(); inline void OnDTOR();
inline void Lock();
inline void Unlock();
public:
template <typename T, typename ...Args>
AuConditional_t<AuIsSame_v<AuResultOf_t<T, Args...>, void>, bool, AuOptional<AuResultOf_t<T, Args...>>>
DoUnderLock(const T &callable, Args && ...args)
{
AU_LOCK_GUARD(this);
if (this->pOwnsLock)
{
if constexpr (AuIsSame_v<AuResultOf_t<T, Args...>, void>)
{
callable(AuForward<Args &&>(args)...);
return true;
}
else
{
return callable(AuForward<Args &&>(args)...);
}
}
else
{
if constexpr (AuIsSame_v<AuResultOf_t<T, Args...>, void>)
{
return false;
}
else
{
return AuOptional<AuResultOf_t<T, Args...>> {};
}
}
}
private:
Threading::Waitables::FutexWaitable selfLock;
DestructionWatch *pParent {}; DestructionWatch *pParent {};
bool bWasCalled {}; Threading::Waitables::FutexWaitable *pOwnsLock {};
}; };
DestructionWatch::DestructionWatch() DestructionWatch::DestructionWatch()
@ -66,26 +105,35 @@ namespace Aurora::Utility
void DestructionWatch::RemoveAll() void DestructionWatch::RemoveAll()
{ {
AU_LOCK_GUARD(this->mutex);
if (auto pWatcher = AuExchange(this->pFirstWatcher, nullptr))
{ {
this->RemoveWatcherCall(pWatcher); AU_LOCK_GUARD(this->mutex);
if (auto pWatcher = AuExchange(this->pFirstWatcher, nullptr))
{
this->RemoveWatcherCall(pWatcher);
}
for (const auto pWatcher : AuExchange(this->listpWatches, {}))
{
this->RemoveWatcherCall(pWatcher);
}
} }
for (const auto pWatcher : AuExchange(this->listpWatches, {}))
{ {
this->RemoveWatcherCall(pWatcher); AU_LOCK_GUARD(this->dtorSignal);
} }
} }
void DestructionWatch::RemoveWatcher(ADestructionWatcher *pWatcher) void DestructionWatch::RemoveWatcher(ADestructionWatcher *pWatcher)
{ {
AU_LOCK_GUARD(this->mutex); AU_LOCK_GUARD(this->mutex);
if (this->pFirstWatcher == pWatcher) if (this->pFirstWatcher == pWatcher)
{ {
this->pFirstWatcher = nullptr; this->pFirstWatcher = nullptr;
return;
} }
AuTryRemove(this->listpWatches, pWatcher); AuTryRemove(this->listpWatches, pWatcher);
} }
@ -118,18 +166,25 @@ namespace Aurora::Utility
this->pParent = pWatch; this->pParent = pWatch;
} }
void ADestructionWatcher::Observe(DestructionWatch &pWatch)
{
this->Observe(&pWatch);
}
bool ADestructionWatcher::IsObservedAlive() bool ADestructionWatcher::IsObservedAlive()
{ {
return !this->bWasCalled; return bool(this->pParent);
} }
bool ADestructionWatcher::IsObservedDead() bool ADestructionWatcher::IsObservedDead()
{ {
return this->bWasCalled; return !bool(this->pParent);
} }
void ADestructionWatcher::OnSelfDTOR() void ADestructionWatcher::OnSelfDTOR()
{ {
AU_LOCK_GUARD(this->selfLock);
if (auto pParent = AuExchange(this->pParent, nullptr)) if (auto pParent = AuExchange(this->pParent, nullptr))
{ {
pParent->RemoveWatcher(this); pParent->RemoveWatcher(this);
@ -138,13 +193,37 @@ namespace Aurora::Utility
void ADestructionWatcher::OnDTOR() void ADestructionWatcher::OnDTOR()
{ {
this->pParent = nullptr;
if (AuExchange(this->bWasCalled, true))
{ {
return; AU_LOCK_GUARD(this->selfLock);
this->pParent = nullptr;
} }
this->OnDestroyedChild(); this->OnDestroyedChild();
} }
void ADestructionWatcher::Lock()
{
AU_LOCK_GUARD(this->selfLock);
this->pOwnsLock = nullptr;
if (!this->pParent)
{
return;
}
this->pParent->dtorSignal.Lock();
// noting that once we leave this scope, we could have our parent stolen from us (this->selfLock), but its' destructor will still be blocked until we finish
// this is why we end up using a raw pointer here.
this->pOwnsLock = &this->pParent->dtorSignal;
}
void ADestructionWatcher::Unlock()
{
if (this->pOwnsLock)
{
this->pOwnsLock->Unlock();
}
}
} }