230 lines
5.3 KiB
C++
230 lines
5.3 KiB
C++
/***
|
|
Copyright (C) 2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: DestructionWatch.hpp
|
|
Date: 2024-01-22
|
|
Author: Reece
|
|
***/
|
|
#pragma once
|
|
|
|
namespace Aurora::Utility
|
|
{
|
|
struct ADestructionWatcher;
|
|
|
|
struct DestructionWatch
|
|
{
|
|
inline DestructionWatch();
|
|
inline ~DestructionWatch();
|
|
|
|
inline void RemoveAll();
|
|
|
|
private:
|
|
friend struct ADestructionWatcher;
|
|
|
|
Threading::Waitables::FutexWaitable mutex;
|
|
Threading::Waitables::FutexWaitable dtorSignal;
|
|
ADestructionWatcher *pFirstWatcher {};
|
|
AuList<ADestructionWatcher *> listpWatches;
|
|
|
|
inline void RemoveWatcher(ADestructionWatcher *pWatcher);
|
|
inline void RemoveWatcherCall(ADestructionWatcher *pWatcher);
|
|
};
|
|
|
|
struct ADestructionWatcher
|
|
{
|
|
inline ADestructionWatcher();
|
|
inline ~ADestructionWatcher();
|
|
|
|
inline void Observe(DestructionWatch *pWatch);
|
|
inline void Observe(DestructionWatch &pWatch);
|
|
inline bool IsObservedAlive();
|
|
inline bool IsObservedDead();
|
|
|
|
protected:
|
|
inline virtual void OnDestroyedChild()
|
|
{
|
|
|
|
}
|
|
|
|
private:
|
|
friend struct DestructionWatch;
|
|
friend class Threading::LockGuard<ADestructionWatcher *>;
|
|
|
|
inline void OnSelfDTOR();
|
|
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 {};
|
|
Threading::Waitables::FutexWaitable *pOwnsLock {};
|
|
};
|
|
|
|
DestructionWatch::DestructionWatch()
|
|
{
|
|
|
|
}
|
|
|
|
DestructionWatch::~DestructionWatch()
|
|
{
|
|
this->RemoveAll();
|
|
}
|
|
|
|
void DestructionWatch::RemoveAll()
|
|
{
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
{
|
|
AU_LOCK_GUARD(this->dtorSignal);
|
|
}
|
|
}
|
|
|
|
void DestructionWatch::RemoveWatcher(ADestructionWatcher *pWatcher)
|
|
{
|
|
AU_LOCK_GUARD(this->mutex);
|
|
|
|
if (this->pFirstWatcher == pWatcher)
|
|
{
|
|
this->pFirstWatcher = nullptr;
|
|
return;
|
|
}
|
|
|
|
AuTryRemove(this->listpWatches, pWatcher);
|
|
}
|
|
|
|
void DestructionWatch::RemoveWatcherCall(ADestructionWatcher *pWatcher)
|
|
{
|
|
pWatcher->OnDTOR();
|
|
}
|
|
|
|
ADestructionWatcher::ADestructionWatcher()
|
|
{
|
|
|
|
}
|
|
|
|
ADestructionWatcher::~ADestructionWatcher()
|
|
{
|
|
this->OnSelfDTOR();
|
|
}
|
|
|
|
void ADestructionWatcher::Observe(DestructionWatch *pWatch)
|
|
{
|
|
AU_LOCK_GUARD(pWatch->mutex);
|
|
if (!pWatch->pFirstWatcher)
|
|
{
|
|
pWatch->pFirstWatcher = this;
|
|
}
|
|
else
|
|
{
|
|
pWatch->listpWatches.push_back(this);
|
|
}
|
|
this->pParent = pWatch;
|
|
}
|
|
|
|
void ADestructionWatcher::Observe(DestructionWatch &pWatch)
|
|
{
|
|
this->Observe(&pWatch);
|
|
}
|
|
|
|
bool ADestructionWatcher::IsObservedAlive()
|
|
{
|
|
return bool(this->pParent);
|
|
}
|
|
|
|
bool ADestructionWatcher::IsObservedDead()
|
|
{
|
|
return !bool(this->pParent);
|
|
}
|
|
|
|
void ADestructionWatcher::OnSelfDTOR()
|
|
{
|
|
AU_LOCK_GUARD(this->selfLock);
|
|
|
|
if (auto pParent = AuExchange(this->pParent, nullptr))
|
|
{
|
|
pParent->RemoveWatcher(this);
|
|
}
|
|
}
|
|
|
|
void ADestructionWatcher::OnDTOR()
|
|
{
|
|
{
|
|
AU_LOCK_GUARD(this->selfLock);
|
|
this->pParent = nullptr;
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|