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 ( ) ;
2024-03-02 00:47:14 +00:00
// If your (optionally abstract) ADestructionWatcher requires access to the parent object, it is highly recommened to call RemoveAll within the destructor of the parent class.
// Otherwise, you must maintain a strict ctor/dtor order of your classes layout.
2024-01-22 02:03:51 +00:00
inline void RemoveAll ( ) ;
private :
friend struct ADestructionWatcher ;
Threading : : Waitables : : FutexWaitable mutex ;
ADestructionWatcher * pFirstWatcher { } ;
2024-03-02 00:47:14 +00:00
AuList < ADestructionWatcher * > * pListpWatches { } ;
AuUInt32 uDtorCalls { } ;
2024-01-22 02:03:51 +00:00
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 ( ) ;
2024-03-17 19:00:05 +00:00
inline ADestructionWatcher ( ADestructionWatcher & & move ) ;
inline ADestructionWatcher ( const ADestructionWatcher & copy ) ;
inline ADestructionWatcher & operator = ( ADestructionWatcher & & move ) ;
inline ADestructionWatcher & operator = ( const ADestructionWatcher & copy ) ;
2024-01-22 02:03:51 +00:00
protected :
2024-03-02 00:47:14 +00:00
// Note that since the parent object is in the midst of destruction; you should not be manually calling the destroy operations of it or its' children.
// C++ construction and the inverse destruction order are well defined.
// In the case of extending DestructionWatch as a base class, our constructor should be invoked first, and therefore our destructor will be invoked last.
// In the case of inserting DestructionWatch as a class member, you must guard resource access via synchronization primitives placed in proper construction/destruction order.
inline virtual void OnParentDestroy ( )
2024-01-22 02:03:51 +00:00
{
}
2024-03-02 00:47:14 +00:00
inline virtual void OnDestroy ( )
{
// You *might* be able to access the parent object at this point in time.
//
// It is legal to access the memory under both OnParentDestroy(), and OnDestroy() in the case that IsObservedAlive() return true; however, accessing the parents
// member fields shall be restricted pursuant to the inverse of C++s construction order. It is possible for multithreaded code to be in the midst of calling
// the destruction chain before we are aware of the destruction condition. Shared pointers work around this by invoking private std::enable_shared_from_this
// members at the time of construction and time of control block release. It *should* be safe to access HANDLEs, raw c pointers, etc without any special logic,
// on the account that the parent object will not have released its' memory until we're done; same applies to OnParentDestroy().
//
// ** To further harden against out of order destruction, it is advisable but not required, to call DestructionWatch::RemoveAll() in the parents destructor. **
// ** Alternatively, you must be hyper-aware of the C++ destruction order. **
}
public :
inline void Destroy ( )
{
if ( ! DoUnderLock ( & ADestructionWatcher : : OnDestroy , this ) )
{
this - > OnDestroy ( ) ;
}
}
2024-01-22 02:03:51 +00:00
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 )
{
{
2024-03-02 00:47:14 +00:00
AU_LOCK_GUARD ( this - > selfLock ) ;
if ( ! this - > pParent )
2024-02-10 05:41:19 +00:00
{
2024-03-02 00:47:14 +00:00
if constexpr ( AuIsSame_v < AuResultOf_t < T , Args . . . > , void > )
{
return false ;
}
else
{
return AuOptional < AuResultOf_t < T , Args . . . > > { } ;
}
2024-02-10 05:41:19 +00:00
}
2024-03-02 00:47:14 +00:00
AuAtomicAdd ( & this - > pParent - > uDtorCalls , 1u ) ;
2024-02-10 05:41:19 +00:00
}
2024-03-02 00:47:14 +00:00
2024-02-10 05:41:19 +00:00
{
2024-03-02 00:47:14 +00:00
AU_LOCK_GUARD ( this ) ;
2024-02-10 05:41:19 +00:00
if constexpr ( AuIsSame_v < AuResultOf_t < T , Args . . . > , void > )
{
2024-03-02 00:47:14 +00:00
AuInvoke ( callable , AuForward < Args & & > ( args ) . . . ) ;
return true ;
2024-02-10 05:41:19 +00:00
}
else
{
2024-03-02 00:47:14 +00:00
return AuInvoke ( callable , AuForward < Args & & > ( args ) . . . ) ;
2024-02-10 05:41:19 +00:00
}
}
}
private :
Threading : : Waitables : : FutexWaitable selfLock ;
2024-01-22 02:03:51 +00:00
DestructionWatch * pParent { } ;
} ;
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 ) ;
}
2024-03-02 00:47:14 +00:00
if ( auto pWatchList = AuExchange ( this - > pListpWatches , nullptr ) )
2024-02-10 05:41:19 +00:00
{
2024-03-02 00:47:14 +00:00
for ( const auto pWatcher : * pWatchList )
{
this - > RemoveWatcherCall ( pWatcher ) ;
}
delete pWatchList ;
2024-02-10 05:41:19 +00:00
}
2024-01-22 02:03:51 +00:00
}
{
2024-03-02 00:47:14 +00:00
AuUInt32 uOld { } ;
while ( ( uOld = AuAtomicLoad ( & this - > uDtorCalls ) ) ! = 0 )
{
Aurora : : Threading : : WaitOnAddress ( & this - > uDtorCalls , & uOld , sizeof ( uOld ) , 0 ) ;
}
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-03-02 00:47:14 +00:00
if ( this - > pListpWatches )
{
AuTryRemove ( * this - > pListpWatches , pWatcher ) ;
}
2024-01-22 02:03:51 +00:00
}
void DestructionWatch : : RemoveWatcherCall ( ADestructionWatcher * pWatcher )
{
pWatcher - > OnDTOR ( ) ;
}
ADestructionWatcher : : ADestructionWatcher ( )
{
}
ADestructionWatcher : : ~ ADestructionWatcher ( )
{
this - > OnSelfDTOR ( ) ;
}
2024-03-17 19:00:05 +00:00
ADestructionWatcher : : ADestructionWatcher ( ADestructionWatcher & & move )
{
AU_LOCK_GUARD ( move . selfLock ) ;
if ( auto pParent = AuExchange ( move . pParent , nullptr ) )
{
pParent - > RemoveWatcher ( & move ) ;
this - > Observe ( pParent ) ;
}
}
ADestructionWatcher : : ADestructionWatcher ( const ADestructionWatcher & copy )
{
AU_LOCK_GUARD ( copy . selfLock ) ;
if ( auto & pParent = copy . pParent )
{
this - > Observe ( pParent ) ;
}
}
ADestructionWatcher & ADestructionWatcher : : operator = ( ADestructionWatcher & & move )
{
AU_LOCK_GUARD ( move . selfLock ) ;
if ( auto pParent = AuExchange ( move . pParent , nullptr ) )
{
pParent - > RemoveWatcher ( & move ) ;
this - > Observe ( pParent ) ;
}
return * this ;
}
ADestructionWatcher & ADestructionWatcher : : operator = ( const ADestructionWatcher & copy )
{
AU_LOCK_GUARD ( copy . selfLock ) ;
if ( auto & pParent = copy . pParent )
{
this - > Observe ( pParent ) ;
}
return * this ;
}
2024-01-22 02:03:51 +00:00
void ADestructionWatcher : : Observe ( DestructionWatch * pWatch )
{
AU_LOCK_GUARD ( pWatch - > mutex ) ;
if ( ! pWatch - > pFirstWatcher )
{
pWatch - > pFirstWatcher = this ;
}
else
{
2024-03-02 00:47:14 +00:00
if ( ! pWatch - > pListpWatches )
{
pWatch - > pListpWatches = new AuRemovePointer_t < decltype ( pWatch - > pListpWatches ) > ( ) ;
}
pWatch - > pListpWatches - > push_back ( this ) ;
2024-01-22 02:03:51 +00:00
}
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 ) )
{
2024-03-02 00:47:14 +00:00
this - > OnDestroy ( ) ;
2024-01-22 02:03:51 +00:00
pParent - > RemoveWatcher ( this ) ;
}
2024-03-02 00:47:14 +00:00
else
{
this - > OnDestroy ( ) ;
}
2024-01-22 02:03:51 +00:00
}
void ADestructionWatcher : : OnDTOR ( )
{
2024-02-10 05:41:19 +00:00
{
AU_LOCK_GUARD ( this - > selfLock ) ;
this - > pParent = nullptr ;
}
2024-03-02 00:47:14 +00:00
this - > OnParentDestroy ( ) ;
2024-02-10 05:41:19 +00:00
}
void ADestructionWatcher : : Lock ( )
{
2024-03-02 00:47:14 +00:00
2024-02-10 05:41:19 +00:00
}
void ADestructionWatcher : : Unlock ( )
{
2024-06-10 13:08:58 +00:00
if ( AuAtomicSub ( & this - > pParent - > uDtorCalls , 1u ) = = 0 )
2024-02-10 05:41:19 +00:00
{
2024-03-02 00:47:14 +00:00
Aurora : : Threading : : WakeOnAddress ( & this - > pParent - > uDtorCalls ) ;
2024-02-10 05:41:19 +00:00
}
2024-01-22 02:03:51 +00:00
}
}