2024-01-22 02:03:51 +00:00
/***
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 ;
2024-02-10 05:41:19 +00:00
Threading : : Waitables : : FutexWaitable dtorSignal ;
2024-01-22 02:03:51 +00:00
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 ) ;
2024-02-10 05:41:19 +00:00
inline void Observe ( DestructionWatch & pWatch ) ;
2024-01-22 02:03:51 +00:00
inline bool IsObservedAlive ( ) ;
inline bool IsObservedDead ( ) ;
protected :
inline virtual void OnDestroyedChild ( )
{
}
private :
friend struct DestructionWatch ;
2024-02-10 05:41:19 +00:00
friend class Threading : : LockGuard < ADestructionWatcher * > ;
2024-01-22 02:03:51 +00:00
inline void OnSelfDTOR ( ) ;
inline void OnDTOR ( ) ;
2024-02-10 05:41:19 +00:00
inline void Lock ( ) ;
inline void Unlock ( ) ;
2024-01-22 02:03:51 +00:00
2024-02-10 05:41:19 +00:00
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 ;
2024-01-22 02:03:51 +00:00
DestructionWatch * pParent { } ;
2024-02-10 05:41:19 +00:00
Threading : : Waitables : : FutexWaitable * pOwnsLock { } ;
2024-01-22 02:03:51 +00:00
} ;
DestructionWatch : : DestructionWatch ( )
{
}
DestructionWatch : : ~ DestructionWatch ( )
{
this - > RemoveAll ( ) ;
}
void DestructionWatch : : RemoveAll ( )
{
{
2024-02-10 05:41:19 +00:00
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 ) ;
}
2024-01-22 02:03:51 +00:00
}
{
2024-02-10 05:41:19 +00:00
AU_LOCK_GUARD ( this - > dtorSignal ) ;
2024-01-22 02:03:51 +00:00
}
}
void DestructionWatch : : RemoveWatcher ( ADestructionWatcher * pWatcher )
{
AU_LOCK_GUARD ( this - > mutex ) ;
2024-02-10 05:41:19 +00:00
2024-01-22 02:03:51 +00:00
if ( this - > pFirstWatcher = = pWatcher )
{
this - > pFirstWatcher = nullptr ;
2024-02-10 05:41:19 +00:00
return ;
2024-01-22 02:03:51 +00:00
}
2024-02-10 05:41:19 +00:00
2024-01-22 02:03:51 +00:00
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 ;
}
2024-02-10 05:41:19 +00:00
void ADestructionWatcher : : Observe ( DestructionWatch & pWatch )
{
this - > Observe ( & pWatch ) ;
}
2024-01-22 02:03:51 +00:00
bool ADestructionWatcher : : IsObservedAlive ( )
{
2024-02-10 05:41:19 +00:00
return bool ( this - > pParent ) ;
2024-01-22 02:03:51 +00:00
}
bool ADestructionWatcher : : IsObservedDead ( )
{
2024-02-10 05:41:19 +00:00
return ! bool ( this - > pParent ) ;
2024-01-22 02:03:51 +00:00
}
void ADestructionWatcher : : OnSelfDTOR ( )
{
2024-02-10 05:41:19 +00:00
AU_LOCK_GUARD ( this - > selfLock ) ;
2024-01-22 02:03:51 +00:00
if ( auto pParent = AuExchange ( this - > pParent , nullptr ) )
{
pParent - > RemoveWatcher ( this ) ;
}
}
void ADestructionWatcher : : OnDTOR ( )
{
2024-02-10 05:41:19 +00:00
{
AU_LOCK_GUARD ( this - > selfLock ) ;
this - > pParent = nullptr ;
}
this - > OnDestroyedChild ( ) ;
}
void ADestructionWatcher : : Lock ( )
{
AU_LOCK_GUARD ( this - > selfLock ) ;
this - > pOwnsLock = nullptr ;
2024-01-22 02:03:51 +00:00
2024-02-10 05:41:19 +00:00
if ( ! this - > pParent )
2024-01-22 02:03:51 +00:00
{
return ;
}
2024-02-10 05:41:19 +00:00
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 ( ) ;
}
2024-01-22 02:03:51 +00:00
}
}