Files
UnrealEngineUWP/Engine/Source/Developer/DerivedDataCache/Private/DerivedDataBackendAsyncPutWrapper.h
Andrew Grant f98f5761cc Copying //UE4/Orion-Staging to //UE4/Main (Origin: //Orion/Dev-General @ 2879808)
==========================
MAJOR FEATURES + CHANGES
==========================

#lockdown Nick.Penwarden

Change 2879705 on 2016/02/24 by Nick.Darnell

	Editor - Tweaking some comments.

	#tests n/a
	#rb n/a

Change 2879674 on 2016/02/24 by Nick.Darnell

	Editor - The editor now supports many new methods of opening new asset editors.  You can choose where tabs open with a great deal more options in Editor Preferences > Appearance > Asset Editor Open Location.  This will reset the 'always open asset editors in new windows' option, it completely replaces and enchances that option.

	#tests Ran the editor, tried each option and they all seem to do what I want.
	#rb matt.kuhlenschmidt

Change 2879661 on 2016/02/24 by Jamie.Dale

	More general fixes for dialogue waves

	- The localization key now uses a hash of the speaker and target voice GUIDs to help keep them short.
	- The localization key can now be user customized, and contains a placeholder format specifier for the context hash.
	- The "Variations" meta-data is now called "Context".

	#rb James.Hopkin
	#tests Built for Windows, Linux, and PS4. Tested a loc gather and export had the correct info in it. Tested the new UI worked as expected.

Change 2879436 on 2016/02/24 by Nicholas.Davies

	A few bug fixes for blocking PS4 > PC chat
	#jira OR-15467 Disable Paragon chat on PS4 for users outside of the game
	#RB Antony.Carter
	#codereview Sam.Zamani
	#TESTS PS4 whispers to and from none Paragon PC users is blocked.

Change 2878929 on 2016/02/23 by Jason.Bestimt

	#ORION_DEV - Merge Main to reconcile 0.20 branch creation

	#RB:none
	#Tests:none

Change 2878600 on 2016/02/23 by Dmitry.Rekman

	Linux: added code to identify CPU for FPSCharts (OR-14949).

	#rb none
	#tests Ran dedicated server on local VM and a few physical boxes.

Change 2878443 on 2016/02/23 by Marcus.Wassmer

	Fix game not ticking when PS button is pressed.
	#rb andrew.grant
	#test golden path ps4

Change 2878361 on 2016/02/23 by Josh.Markiewicz

	#UE4 - fixed bad comment
	#rb none
	#tests none

Change 2878205 on 2016/02/23 by Jason.Bestimt

	#ORION_DEV - Merge main (0.19) at CL# 2878162

	#Tests:none
	#RB:none

Change 2878095 on 2016/02/23 by Josh.Markiewicz

	#UE4 - added warnings to json mcp read/write failures
	- removed HostAddressOverride parameter (use -uselocalips and -multihome together instead)
	#rb none
	#tests matchmaking golden path

Change 2878002 on 2016/02/23 by Josh.Markiewicz

	#UE4 - made two party framework functions virtual
	#rb none
	#tests none

Change 2877998 on 2016/02/23 by Josh.Markiewicz

	#Ue4 - Party interface can optionally enable/disable creating a chat room alongside the party (defaults to enabled)
	#rb rob.cannaday
	#tests social/team parties golden path
	#codereview rob.cannaday

Change 2877822 on 2016/02/23 by Olaf.Piesche

	speculative fix for OR-15710

	#rb david.hill
	#tests PC game

Change 2877804 on 2016/02/23 by Uriel.Doyon

	Fixed ULevel::AddReferencedObjects clearing all references to static texture streaming data
	#codereview robert.manuszewski
	#rb marcus.wassmer
	#tests played several games on PC, also doing rejoin
	#jira OR-15658

Change 2877692 on 2016/02/23 by Jamie.Dale

	Added commandlet to replace sound wave players in sound cues with dialogue wave players where appropriate

	#rb Saul.Abreu
	#tests Built for Windows, Linux, and PS4. Tested the commandlet.

Change 2877691 on 2016/02/23 by Jamie.Dale

	Added commandlet to extract out the information from our character sheets and put it into the correct dialogue waves

	#rb Saul.Abreu
	#tests Built for Windows, Linux, and PS4. Tested the commandlet.

Change 2877690 on 2016/02/23 by Jamie.Dale

	General dialogue wave fixes

[CL 2881965 by Andrew Grant in Main branch]
2016-02-25 15:13:33 -05:00

272 lines
8.8 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
#pragma once
#include "DerivedDataCacheUsageStats.h"
/**
* Thread safe set helper
**/
struct FThreadSet
{
FCriticalSection SynchronizationObject;
TSet<FString> FilesInFlight;
void Add(const FString& Key)
{
FScopeLock ScopeLock(&SynchronizationObject);
check(Key.Len());
FilesInFlight.Add(Key);
}
void Remove(const FString& Key)
{
FScopeLock ScopeLock(&SynchronizationObject);
FilesInFlight.Remove(Key);
}
bool Exists(const FString& Key)
{
FScopeLock ScopeLock(&SynchronizationObject);
return FilesInFlight.Contains(Key);
}
bool AddIfNotExists(const FString& Key)
{
FScopeLock ScopeLock(&SynchronizationObject);
check(Key.Len());
if (!FilesInFlight.Contains(Key))
{
FilesInFlight.Add(Key);
return true;
}
return false;
}
};
/**
* Async task to handle the fire and forget async put
**/
class FCachePutAsyncWorker
{
public:
/** Cache Key for the put to InnerBackend **/
FString CacheKey;
/** Data for the put to InnerBackend **/
TArray<uint8> Data;
/** Backend to use for storage, my responsibilities are about async puts **/
FDerivedDataBackendInterface* InnerBackend;
/** Memory based cache to clear once the put is finished **/
FDerivedDataBackendInterface* InflightCache;
/** We remember outstanding puts so that we don't do them redundantly **/
FThreadSet* FilesInFlight;
/**If true, then do not attempt skip the put even if CachedDataProbablyExists returns true **/
bool bPutEvenIfExists;
/** Usage stats to track thread times. */
FDerivedDataCacheUsageStats& UsageStats;
/** Constructor
*/
FCachePutAsyncWorker(const TCHAR* InCacheKey, const TArray<uint8>* InData, FDerivedDataBackendInterface* InInnerBackend, bool InbPutEvenIfExists, FDerivedDataBackendInterface* InInflightCache, FThreadSet* InInFilesInFlight, FDerivedDataCacheUsageStats& InUsageStats)
: CacheKey(InCacheKey)
, Data(*InData)
, InnerBackend(InInnerBackend)
, InflightCache(InInflightCache)
, FilesInFlight(InInFilesInFlight)
, bPutEvenIfExists(InbPutEvenIfExists)
, UsageStats(InUsageStats)
{
check(InnerBackend);
}
/** Call the inner backend and when that completes, remove the memory cache */
void DoWork()
{
COOK_STAT(auto Timer = UsageStats.TimePut());
bool bOk = true;
const bool bAlreadyExists = InnerBackend->CachedDataProbablyExists(*CacheKey);
if (!bAlreadyExists || bPutEvenIfExists)
{
InnerBackend->PutCachedData(*CacheKey, Data, bPutEvenIfExists);
COOK_STAT(Timer.AddHit(Data.Num()));
}
// if it already existed, don't bother checking if we need to retry. We don't.
if (InflightCache && !bAlreadyExists && !InnerBackend->CachedDataProbablyExists(*CacheKey))
{
// retry
InnerBackend->PutCachedData(*CacheKey, Data, false);
if (!InnerBackend->CachedDataProbablyExists(*CacheKey))
{
UE_LOG(LogDerivedDataCache, Warning, TEXT("FDerivedDataBackendAsyncPutWrapper: Put failed, keeping in memory copy %s."),*CacheKey);
bOk = false;
}
}
if (bOk && InflightCache)
{
InflightCache->RemoveCachedData(*CacheKey, /*bTransient=*/ false); // we can remove this from the temp cache, since the real cache will hit now
}
FilesInFlight->Remove(CacheKey);
FDerivedDataBackend::Get().AddToAsyncCompletionCounter(-1);
}
FORCEINLINE TStatId GetStatId() const
{
RETURN_QUICK_DECLARE_CYCLE_STAT(FCachePutAsyncWorker, STATGROUP_ThreadPoolAsyncTasks);
}
/** Indicates to the thread pool that this task is abandonable */
bool CanAbandon()
{
return true;
}
/** Abandon routine, we need to remove the item from the in flight cache because something might be waiting for that */
void Abandon()
{
if (InflightCache)
{
InflightCache->RemoveCachedData(*CacheKey, /*bTransient=*/ false); // we can remove this from the temp cache, since the real cache will hit now
}
FilesInFlight->Remove(CacheKey);
FDerivedDataBackend::Get().AddToAsyncCompletionCounter(-1);
}
};
/**
* A backend wrapper that coordinates async puts. This means that a Get will hit an in-memory cache while the async put is still in flight.
**/
class FDerivedDataBackendAsyncPutWrapper : public FDerivedDataBackendInterface
{
public:
/**
* Constructor
*
* @param InInnerBackend Backend to use for storage, my responsibilities are about async puts
* @param bCacheInFlightPuts if true, cache in-flight puts in a memory cache so that they hit immediately
*/
FDerivedDataBackendAsyncPutWrapper(FDerivedDataBackendInterface* InInnerBackend, bool bCacheInFlightPuts)
: InnerBackend(InInnerBackend)
, InflightCache(bCacheInFlightPuts ? (new FMemoryDerivedDataBackend) : NULL)
{
check(InnerBackend);
}
/** return true if this cache is writable **/
virtual bool IsWritable() override
{
return InnerBackend->IsWritable();
}
/**
* Synchronous test for the existence of a cache item
*
* @param CacheKey Alphanumeric+underscore key of this cache item
* @return true if the data probably will be found, this can't be guaranteed because of concurrency in the backends, corruption, etc
*/
virtual bool CachedDataProbablyExists(const TCHAR* CacheKey) override
{
COOK_STAT(auto Timer = UsageStats.TimeProbablyExists());
bool Result = (InflightCache && InflightCache->CachedDataProbablyExists(CacheKey)) || InnerBackend->CachedDataProbablyExists(CacheKey);
COOK_STAT(if (Result) { Timer.AddHit(0); });
return Result;
}
/**
* Synchronous retrieve of a cache item
*
* @param CacheKey Alphanumeric+underscore key of this cache item
* @param OutData Buffer to receive the results, if any were found
* @return true if any data was found, and in this case OutData is non-empty
*/
virtual bool GetCachedData(const TCHAR* CacheKey, TArray<uint8>& OutData) override
{
COOK_STAT(auto Timer = UsageStats.TimeGet());
if (InflightCache && InflightCache->GetCachedData(CacheKey, OutData))
{
COOK_STAT(Timer.AddHit(OutData.Num()));
return true;
}
bool bSuccess = InnerBackend->GetCachedData(CacheKey, OutData);
if (bSuccess)
{
COOK_STAT(Timer.AddHit(OutData.Num()));
}
return bSuccess;
}
/**
* Asynchronous, fire-and-forget placement of a cache item
*
* @param CacheKey Alphanumeric+underscore key of this cache item
* @param InData Buffer containing the data to cache, can be destroyed after the call returns, immediately
* @param bPutEvenIfExists If true, then do not attempt skip the put even if CachedDataProbablyExists returns true
*/
virtual void PutCachedData(const TCHAR* CacheKey, TArray<uint8>& InData, bool bPutEvenIfExists) override
{
COOK_STAT(auto Timer = PutSyncUsageStats.TimePut());
if (!InnerBackend->IsWritable())
{
return; // no point in continuing down the chain
}
const bool bAdded = FilesInFlight.AddIfNotExists(CacheKey);
if (!bAdded)
{
return; // if it is already on its way, we don't need to send it again
}
if (InflightCache)
{
if (InflightCache->CachedDataProbablyExists(CacheKey))
{
return; // if it is already on its way, we don't need to send it again
}
InflightCache->PutCachedData(CacheKey, InData, true); // temp copy stored in memory while the async task waits to complete
COOK_STAT(Timer.AddHit(InData.Num()));
}
FDerivedDataBackend::Get().AddToAsyncCompletionCounter(1);
(new FAutoDeleteAsyncTask<FCachePutAsyncWorker>(CacheKey, &InData, InnerBackend, bPutEvenIfExists, InflightCache.GetOwnedPointer(), &FilesInFlight, UsageStats))->StartBackgroundTask();
}
virtual void RemoveCachedData(const TCHAR* CacheKey, bool bTransient) override
{
if (!InnerBackend->IsWritable())
{
return; // no point in continuing down the chain
}
while (FilesInFlight.Exists(CacheKey))
{
FPlatformProcess::Sleep(0.0f); // this is an exception condition (corruption), spin and wait for it to clear
}
if (InflightCache)
{
InflightCache->RemoveCachedData(CacheKey, bTransient);
}
InnerBackend->RemoveCachedData(CacheKey, bTransient);
}
virtual void GatherUsageStats(TMap<FString, FDerivedDataCacheUsageStats>& UsageStatsMap, FString&& GraphPath) override
{
COOK_STAT(
{
UsageStatsMap.Add(GraphPath + TEXT(": AsyncPut"), UsageStats);
UsageStatsMap.Add(GraphPath + TEXT(": AsyncPutSync"), PutSyncUsageStats);
if (InnerBackend)
{
InnerBackend->GatherUsageStats(UsageStatsMap, GraphPath + TEXT(". 0"));
}
if (InflightCache)
{
InflightCache->GatherUsageStats(UsageStatsMap, GraphPath + TEXT(". 1"));
}
});
}
private:
FDerivedDataCacheUsageStats UsageStats;
FDerivedDataCacheUsageStats PutSyncUsageStats;
/** Backend to use for storage, my responsibilities are about async puts **/
FDerivedDataBackendInterface* InnerBackend;
/** Memory based cache to deal with gets that happen while an async put is still in flight **/
TScopedPointer<FDerivedDataBackendInterface> InflightCache;
/** We remember outstanding puts so that we don't do them redundantly **/
FThreadSet FilesInFlight;
};