2020-06-23 18:40:00 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
# include "CookPlatformManager.h"
# include "Commandlets/AssetRegistryGenerator.h"
# include "CookOnTheSide/CookOnTheFlyServer.h"
# include "CookRequests.h"
# include "Interfaces/ITargetPlatform.h"
2020-09-24 00:43:27 -04:00
# include "Interfaces/ITargetPlatformManagerModule.h"
# include "Misc/ScopeRWLock.h"
2020-06-23 18:40:00 -04:00
namespace UE
{
namespace Cook
{
2020-09-24 00:43:27 -04:00
class FRWScopeLockConditional
{
public :
FRWScopeLockConditional ( FRWLock & InLockObject , FRWScopeLockType InLockType , bool bInNeedsLock )
: LockObject ( InLockObject )
, LockType ( InLockType )
, bNeedsLock ( bInNeedsLock )
{
if ( bNeedsLock )
{
if ( LockType ! = SLT_ReadOnly )
{
LockObject . WriteLock ( ) ;
}
else
{
LockObject . ReadLock ( ) ;
}
}
}
~ FRWScopeLockConditional ( )
{
if ( bNeedsLock )
{
if ( LockType ! = SLT_ReadOnly )
{
LockObject . WriteUnlock ( ) ;
}
else
{
LockObject . ReadUnlock ( ) ;
}
}
}
private :
UE_NONCOPYABLE ( FRWScopeLockConditional ) ;
FRWLock & LockObject ;
FRWScopeLockType LockType ;
bool bNeedsLock ;
} ;
FPlatformData : : FPlatformData ( )
: LastReferenceTime ( 0.0 )
, ReferenceCount ( 0 )
2020-06-23 18:40:00 -04:00
{
}
2020-09-24 00:43:27 -04:00
uint32 FPlatformManager : : IsInPlatformsLockTLSSlot = 0 ;
void FPlatformManager : : InitializeTls ( )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
IsInPlatformsLockTLSSlot = FPlatformTLS : : AllocTlsSlot ( ) ;
2020-06-23 18:40:00 -04:00
}
2020-09-24 00:43:27 -04:00
bool FPlatformManager : : IsInPlatformsLock ( )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
return FPlatformTLS : : GetTlsValue ( IsInPlatformsLockTLSSlot ) ! = 0 ;
}
void FPlatformManager : : SetIsInPlatformsLock ( bool bValue )
{
FPlatformTLS : : SetTlsValue ( IsInPlatformsLockTLSSlot , bValue ? ( void * ) 0x1 : ( void * ) 0x0 ) ;
}
FPlatformManager : : ~ FPlatformManager ( )
{
{
FRWScopeLock PlatformDatasScopeLock ( PlatformDatasLock , FRWScopeLockType : : SLT_Write ) ;
for ( TPair < const ITargetPlatform * , FPlatformData * > & kvpair : PlatformDatas )
{
2021-09-02 08:40:46 -04:00
FPlatformData * Value = kvpair . Value ;
delete Value ;
2020-09-24 00:43:27 -04:00
}
PlatformDatas . Empty ( ) ;
PlatformDatasByName . Empty ( ) ;
}
}
const TArray < FPlatformId > & FPlatformManager : : GetSessionPlatforms ( ) const
{
checkf ( IsSchedulerThread ( ) , TEXT ( " Access to SessionPlatforms on non-scheduler thread that persists outside of function scope is not yet implemented. " ) ) ;
2020-06-23 18:40:00 -04:00
checkf ( bHasSelectedSessionPlatforms , TEXT ( " Calling GetSessionPlatforms or (any of the top level cook functions that call it) without first calling SelectSessionPlatforms is invalid " ) ) ;
return SessionPlatforms ;
}
2020-09-24 00:43:27 -04:00
bool FPlatformManager : : HasSessionPlatform ( FPlatformId TargetPlatform ) const
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
const bool bIsSchedulerThread = IsSchedulerThread ( ) ;
checkf ( bIsSchedulerThread | | IsInPlatformsLock ( ) , TEXT ( " Looking up platforms by PlatformId is only legal on non-scheduler threads when inside a ReadLockPlatforms scope. " ) ) ;
FRWScopeLockConditional ScopeLock ( SessionLock , FRWScopeLockType : : SLT_ReadOnly , ! bIsSchedulerThread ) ;
2020-06-23 18:40:00 -04:00
return SessionPlatforms . Contains ( TargetPlatform ) ;
}
2022-05-20 16:12:27 -04:00
void FPlatformManager : : SelectSessionPlatforms ( UCookOnTheFlyServer & COTFS , const TArrayView < FPlatformId const > & TargetPlatforms )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
checkf ( IsSchedulerThread ( ) , TEXT ( " Writing to SessionPlatforms is only allowed from the scheduler thread. " ) ) ;
for ( FPlatformId TargetPlatform : TargetPlatforms )
2020-06-23 18:40:00 -04:00
{
CreatePlatformData ( TargetPlatform ) ;
}
2020-09-24 00:43:27 -04:00
FRWScopeLock ScopeLock ( SessionLock , FRWScopeLockType : : SLT_Write ) ;
SessionPlatforms . Empty ( TargetPlatforms . Num ( ) ) ;
SessionPlatforms . Append ( TargetPlatforms . GetData ( ) , TargetPlatforms . Num ( ) ) ;
2022-05-20 16:12:27 -04:00
COTFS . bSessionRunning = true ;
2020-06-23 18:40:00 -04:00
bHasSelectedSessionPlatforms = true ;
}
2022-05-20 16:12:27 -04:00
void FPlatformManager : : ClearSessionPlatforms ( UCookOnTheFlyServer & COTFS )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
checkf ( IsSchedulerThread ( ) , TEXT ( " Writing to SessionPlatforms is only allowed from the scheduler thread. " ) ) ;
FRWScopeLock ScopeLock ( SessionLock , FRWScopeLockType : : SLT_Write ) ;
2020-06-23 18:40:00 -04:00
SessionPlatforms . Empty ( ) ;
2022-05-20 16:12:27 -04:00
COTFS . bSessionRunning = false ;
2020-06-23 18:40:00 -04:00
bHasSelectedSessionPlatforms = false ;
}
2022-05-20 16:12:27 -04:00
void FPlatformManager : : AddSessionPlatform ( UCookOnTheFlyServer & COTFS , FPlatformId TargetPlatform )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
checkf ( IsSchedulerThread ( ) , TEXT ( " Writing to SessionPlatforms is only allowed from the scheduler thread. " ) ) ;
CreatePlatformData ( TargetPlatform ) ;
2020-06-23 18:40:00 -04:00
2020-09-24 00:43:27 -04:00
FRWScopeLock ScopeLock ( SessionLock , FRWScopeLockType : : SLT_Write ) ;
if ( SessionPlatforms . Contains ( TargetPlatform ) )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
return ;
2020-06-23 18:40:00 -04:00
}
2020-09-24 00:43:27 -04:00
SessionPlatforms . Add ( TargetPlatform ) ;
2022-05-20 16:12:27 -04:00
COTFS . bSessionRunning = true ;
2020-09-24 00:43:27 -04:00
bHasSelectedSessionPlatforms = true ;
2020-06-23 18:40:00 -04:00
}
2020-09-24 00:43:27 -04:00
FPlatformData * FPlatformManager : : GetPlatformData ( FPlatformId Platform )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
const bool bIsSchedulerThread = IsSchedulerThread ( ) ;
checkf ( bIsSchedulerThread | | IsInPlatformsLock ( ) , TEXT ( " Reading FPlatformData on non-scheduler threads is only allowed when inside a ReadLockPlatforms scope. " ) ) ;
FPlatformData * * Existing = PlatformDatas . Find ( Platform ) ;
return Existing ? * Existing : nullptr ;
}
FPlatformData * FPlatformManager : : GetPlatformDataByName ( FName PlatformName )
{
const bool bIsSchedulerThread = IsSchedulerThread ( ) ;
checkf ( bIsSchedulerThread | | IsInPlatformsLock ( ) , TEXT ( " Reading FPlatformData on non-scheduler threads is only allowed when inside a ReadLockPlatforms scope. " ) ) ;
FPlatformData * * Existing = PlatformDatasByName . Find ( PlatformName ) ;
return Existing ? * Existing : nullptr ;
2020-06-23 18:40:00 -04:00
}
FPlatformData & FPlatformManager : : CreatePlatformData ( const ITargetPlatform * Platform )
{
check ( Platform ! = nullptr ) ;
2020-09-24 00:43:27 -04:00
FPlatformData * * ExistingPlatformData = PlatformDatas . Find ( Platform ) ;
if ( ExistingPlatformData )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
return * * ExistingPlatformData ;
}
checkf ( IsSchedulerThread ( ) , TEXT ( " Writing to FPlatformData is only allowed from the scheduler thread. " ) ) ;
FName PlatformName ( Platform - > PlatformName ( ) ) ;
checkf ( ! PlatformName . IsNone ( ) , TEXT ( " Invalid ITargetPlatform with an empty name " ) ) ;
{
FRWScopeLock PlatformDatasScopeLock ( PlatformDatasLock , FRWScopeLockType : : SLT_Write ) ;
FPlatformData * & PlatformData = PlatformDatas . Add ( Platform ) ;
PlatformData = new FPlatformData ;
PlatformDatasByName . Add ( PlatformName , PlatformData ) ;
// We could get the non-const ITargetPlatform* from the global PlatformTargetModule, so this const cast is just a performance shortcut rather than a contract break
ITargetPlatform * NonConstPlatform = const_cast < ITargetPlatform * > ( Platform ) ;
PlatformData - > TargetPlatform = NonConstPlatform ;
PlatformData - > PlatformName = PlatformName ;
return * PlatformData ;
2020-06-23 18:40:00 -04:00
}
}
2020-09-24 00:43:27 -04:00
bool FPlatformManager : : IsPlatformInitialized ( FPlatformId Platform ) const
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
checkf ( IsSchedulerThread ( ) | | IsInPlatformsLock ( ) , TEXT ( " Looking up platforms by PlatformId is only legal on non-scheduler threads when inside a ReadLockPlatforms scope. " ) ) ;
const FPlatformData * const * PlatformData = PlatformDatas . Find ( Platform ) ;
2020-06-23 18:40:00 -04:00
if ( ! PlatformData )
{
return false ;
}
2020-09-24 00:43:27 -04:00
return ( * PlatformData ) - > bIsSandboxInitialized ;
2020-06-23 18:40:00 -04:00
}
2020-09-24 00:43:27 -04:00
void FPlatformManager : : SetArePlatformsPrepopulated ( bool bValue )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
checkf ( IsSchedulerThread ( ) , TEXT ( " Get/SetArePlatformsPrepopulated is only allowed from the scheduler thread. " ) ) ;
bArePlatformsPrepopulated = bValue ;
}
bool FPlatformManager : : GetArePlatformsPrepopulated ( ) const
{
checkf ( IsSchedulerThread ( ) , TEXT ( " Get/SetArePlatformsPrepopulated is only allowed from the scheduler thread. " ) ) ;
return bArePlatformsPrepopulated ;
2020-06-23 18:40:00 -04:00
}
void FPlatformManager : : PruneUnreferencedSessionPlatforms ( UCookOnTheFlyServer & CookOnTheFlyServer )
{
2020-09-24 00:43:27 -04:00
checkf ( IsSchedulerThread ( ) , TEXT ( " Writing to SessionPlatforms is only allowed from the scheduler thread. " ) ) ;
2020-06-23 18:40:00 -04:00
const double SecondsToLive = 5.0 * 60 ;
double OldestKeepTime = - 1.0e10 ; // Constructed to something smaller than 0 - SecondsToLive, so we can robustly detect not-yet-initialized
TArray < const ITargetPlatform * , TInlineAllocator < 1 > > RemovePlatforms ;
2020-09-24 00:43:27 -04:00
for ( TPair < const ITargetPlatform * , FPlatformData * > & kvpair : PlatformDatas )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
FPlatformData * PlatformData = kvpair . Value ;
if ( PlatformData - > LastReferenceTime > 0. & & PlatformData - > ReferenceCount = = 0 )
2020-06-23 18:40:00 -04:00
{
// We have a platform that we need to check for pruning. Initialize the OldestKeepTime so we can check whether the platform has aged out.
if ( OldestKeepTime < - SecondsToLive )
{
const double CurrentTimeSeconds = FPlatformTime : : Seconds ( ) ;
OldestKeepTime = CurrentTimeSeconds - SecondsToLive ;
}
// Note that this loop is outside of the critical section, for performance.
// If we find any candidates for pruning we have to check them again once inside the critical section.
2020-09-24 00:43:27 -04:00
if ( kvpair . Value - > LastReferenceTime < OldestKeepTime )
2020-06-23 18:40:00 -04:00
{
RemovePlatforms . Add ( kvpair . Key ) ;
}
}
}
if ( RemovePlatforms . Num ( ) > 0 )
{
2020-09-24 00:43:27 -04:00
FRWScopeLock Lock ( SessionLock , FRWScopeLockType : : SLT_Write ) ;
2020-06-23 18:40:00 -04:00
for ( const ITargetPlatform * TargetPlatform : RemovePlatforms )
{
2020-09-24 00:43:27 -04:00
FPlatformData * PlatformData = * PlatformDatas . Find ( TargetPlatform ) ;
2020-06-23 18:40:00 -04:00
if ( PlatformData - > LastReferenceTime > 0. & & PlatformData - > ReferenceCount = = 0 & & PlatformData - > LastReferenceTime < OldestKeepTime )
{
// Mark that the platform no longer needs to be inspected for pruning because we have removed it from CookOnTheFly's SessionPlatforms
PlatformData - > LastReferenceTime = 0. ;
// Remove the SessionPlatform
CookOnTheFlyServer . OnRemoveSessionPlatform ( TargetPlatform ) ;
SessionPlatforms . Remove ( TargetPlatform ) ;
if ( SessionPlatforms . Num ( ) = = 0 )
{
2022-05-20 16:12:27 -04:00
CookOnTheFlyServer . bSessionRunning = false ;
2020-06-23 18:40:00 -04:00
bHasSelectedSessionPlatforms = false ;
}
}
}
}
}
2020-09-24 00:43:27 -04:00
void FPlatformManager : : AddRefCookOnTheFlyPlatform ( FName PlatformName , UCookOnTheFlyServer & CookOnTheFlyServer )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
checkf ( IsSchedulerThread ( ) | | IsInPlatformsLock ( ) , TEXT ( " AddRefCookOnTheFlyPlatform is only legal on non-scheduler threads when inside a ReadLockPlatforms scope. " ) ) ;
FPlatformData * PlatformData = GetPlatformDataByName ( PlatformName ) ;
checkf ( PlatformData ! = nullptr , TEXT ( " Unrecognized Platform %s " ) , * PlatformName . ToString ( ) ) ;
2020-06-23 18:40:00 -04:00
+ + PlatformData - > ReferenceCount ;
2020-09-24 00:43:27 -04:00
if ( ! HasSessionPlatform ( PlatformData - > TargetPlatform ) )
2020-06-23 18:40:00 -04:00
{
2022-05-27 16:48:59 -04:00
CookOnTheFlyServer . ExternalRequests - > AddCallback ( [ PlatformName , LocalCookOnTheFlyServer = & CookOnTheFlyServer ] ( )
2020-06-23 18:40:00 -04:00
{
2022-05-27 16:48:59 -04:00
ITargetPlatform * TargetPlatform = GetTargetPlatformManager ( ) - > FindTargetPlatform ( PlatformName . ToString ( ) ) ;
2020-09-24 00:43:27 -04:00
if ( TargetPlatform )
{
2022-05-27 16:48:59 -04:00
LocalCookOnTheFlyServer - > StartCookOnTheFlySessionFromGameThread ( TargetPlatform ) ;
2020-09-24 00:43:27 -04:00
}
2020-06-23 18:40:00 -04:00
} ) ;
}
}
2020-09-24 00:43:27 -04:00
void FPlatformManager : : ReleaseCookOnTheFlyPlatform ( FName PlatformName )
2020-06-23 18:40:00 -04:00
{
2020-09-24 00:43:27 -04:00
checkf ( IsSchedulerThread ( ) | | IsInPlatformsLock ( ) , TEXT ( " ReleaseCookOnTheFlyPlatform is only legal on non-scheduler threads when inside a ReadLockPlatforms scope. " ) ) ;
FPlatformData * PlatformData = GetPlatformDataByName ( PlatformName ) ;
checkf ( PlatformData ! = nullptr , TEXT ( " Unrecognized Platform %s " ) , * PlatformName . ToString ( ) ) ;
2020-06-23 18:40:00 -04:00
check ( PlatformData - > ReferenceCount > 0 ) ;
- - PlatformData - > ReferenceCount ;
PlatformData - > LastReferenceTime = FPlatformTime : : Seconds ( ) ;
}
2020-09-24 00:43:27 -04:00
TMap < ITargetPlatform * , ITargetPlatform * > FPlatformManager : : RemapTargetPlatforms ( )
{
checkf ( IsSchedulerThread ( ) , TEXT ( " Writing to PlatformDatas is only allowed from the scheduler thread. " ) ) ;
TMap < ITargetPlatform * , ITargetPlatform * > Remap ;
ITargetPlatformManagerModule * PlatformManager = GetTargetPlatformManager ( ) ;
TFastPointerMap < const ITargetPlatform * , FPlatformData * > NewPlatformDatas ;
{
FRWScopeLock PlatformDatasScopeLock ( PlatformDatasLock , FRWScopeLockType : : SLT_Write ) ;
for ( TPair < const ITargetPlatform * , FPlatformData * > & kvpair : PlatformDatas )
{
ITargetPlatform * Old = const_cast < ITargetPlatform * > ( kvpair . Key ) ;
FPlatformData & Data = * kvpair . Value ;
FName PlatformName = Data . PlatformName ;
ITargetPlatform * New = PlatformManager - > FindTargetPlatform ( PlatformName . ToString ( ) ) ;
checkf ( New , TEXT ( " TargetPlatform %s has been removed from the list of TargetPlatforms from ITargetPlatformManagerModule after cooking has started; this case is not handled. " ) , * PlatformName . ToString ( ) ) ;
Data . TargetPlatform = New ;
Remap . Add ( Old , New ) ;
NewPlatformDatas . Add ( New , & Data ) ;
}
// Note that PlatformDatasByName is unchanged, since it maps PlatformName -> PlatformData*, and neither of those have changed. The values inside the FPlatformData may have changed, though, so we need to hold the lock during this function.
Swap ( PlatformDatas , NewPlatformDatas ) ;
{
FRWScopeLock SessionScopeLock ( SessionLock , FRWScopeLockType : : SLT_Write ) ;
RemapArrayElements ( SessionPlatforms , Remap ) ;
}
}
return Remap ;
}
FPlatformManager : : FReadScopeLock : : FReadScopeLock ( FPlatformManager & InPlatformManager )
: PlatformManager ( InPlatformManager )
{
bAttached = true ;
PlatformManager . PlatformDatasLock . ReadLock ( ) ;
check ( ! IsInPlatformsLock ( ) ) ;
SetIsInPlatformsLock ( true ) ;
}
FPlatformManager : : FReadScopeLock : : FReadScopeLock ( FReadScopeLock & & Other )
: PlatformManager ( Other . PlatformManager )
{
bAttached = Other . bAttached ;
Other . bAttached = false ;
}
FPlatformManager : : FReadScopeLock : : ~ FReadScopeLock ( )
{
if ( bAttached )
{
SetIsInPlatformsLock ( false ) ;
PlatformManager . PlatformDatasLock . ReadUnlock ( ) ;
bAttached = false ;
}
}
FPlatformManager : : FReadScopeLock FPlatformManager : : ReadLockPlatforms ( )
{
return FReadScopeLock ( * this ) ;
}
2020-06-23 18:40:00 -04:00
}
}