Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
PreloadableFile.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "Containers/Array.h"
6#include "Containers/Map.h"
7#include "Containers/StringFwd.h"
8#include "Containers/StringView.h"
9#include "Containers/UnrealString.h"
10#include "CoreTypes.h"
11#include "HAL/CriticalSection.h"
12#include "Misc/AssertionMacros.h"
13#include "Serialization/Archive.h"
14#include "Templates/Atomic.h"
15#include "Templates/Function.h"
16#include "Templates/SharedPointer.h"
17#include "Templates/UniquePtr.h"
18
19class FEvent;
20class IAsyncReadFileHandle;
21class IAsyncReadRequest;
22class IFileHandle;
23
24#define FPRELOADABLEFILE_TEST_ENABLED 0
25
26/**
27 * A read-only archive that adds support for asynchronous preloading and priming to an inner archive.
28 *
29 * This class supports two mutually-exclusive modes:
30 * PreloadBytes:
31 * An asynchronous inner archive is opened using the passed-in CreateAsyncArchive function; this call is made asynchronously on a TaskGraph thread.
32 * The size is read during initialization.
33 * After initialization, when StartPreload is called, an array of bytes equal in size to the inner archive's size is allocated,
34 * and an asynchronous ReadRequest is sent to the IAsyncReadFileHandle to read the first <PageSize> bytes of the file.
35 * Upon completion of each in-flight ReadRequest, another asynchronous ReadRequest is issued, until the entire file has been read.
36 * If serialize functions are called beyond the bytes of the file that have been cached so far, they requests are satisfied by synchronous reads.
37 *
38 * Activate this mode by passing an FCreateArchive to InitializeAsync.
39 * PreloadHandle:
40 * A synchronous inner archive is opened using the passed-in CreateArchive function; this call is made asynchronously on a TaskGraph thread.
41 * Optionally, a precache request is sent to the inner archive for the first <PrimeSize> bytes; this call is also made asynchronously.
42 * The created and primed lower-level FArchive can then be detached from this class and handed off to a new owner.
43 *
44 * Activate this mode by passing an FCreateAsyncArchive and (optionally, for the precache request) Flags::Prime to InitializeAsync.
45 *
46 * This class is not threadsafe. The public interface can be used at the same time as internal asynchronous tasks are executing, but the
47 * public interface can not be used from multiple threads at once.
48 */
50{
51public:
52 typedef TUniqueFunction<FArchive* ()> FCreateArchive;
53 typedef TUniqueFunction<IAsyncReadFileHandle* ()> FCreateAsyncArchive;
54
55 enum Flags
56 {
57 None = 0x0,
58
59 // Mode (mutually exclusive)
60 ModeBits = 0x1,
61 PreloadHandle = 0x0, // Asynchronously open the Lower-Level Archive, but do not read bytes from it. The Lower-Level Archive can be detached or can be accessed through Serialize.
62 PreloadBytes = 0x1, // Asynchronously open the Lower-Level Archive and read bytes from it into an In-Memory cache of the file. Serialize will read from the cached bytes if available, otherwise will read from the Lower-Level Archive.
63
64 // Options (independently selectable, do not necessarily apply to all modes)
65 Prime = 0x2, // Only applicable to PreloadHandle mode. After asynchronously opening the LowerLevel archive, asychronously call Precache(0, <PrimeSize>).
66 };
67 enum
68 {
69 DefaultPrimeSize = 1024, // The default argument for PrimeSize in IntializeAsync. How many bytes are requested in the initial PrimeSize call.
70 DefaultPageSize = 64*1024 // The default size of read requests made to the LowerLevel Archive in PreloadBytes mode when reading bytes into the In-Memory cache.
71 };
72
73 FPreloadableArchive(FStringView ArchiveName);
75
76 // Initialization
77 /** Set the PageSize used read requests made to the LowerLevel Archive in PreloadBytes mode when reading bytes into the In-Memory cache. Invalid to set after Initialization; PageSize must be constant during use. */
78 void SetPageSize(int64 PageSize);
79 /** Initialize the FPreloadableFile asynchronously, performing FileOpen operations on another thread. Use IsInitialized or WaitForInitialization to check progress. */
80 void InitializeAsync(FCreateArchive&& InCreateArchiveFunction, uint32 InFlags = Flags::None, int64 PrimeSize = DefaultPrimeSize);
81 void InitializeAsync(FCreateAsyncArchive&& InCreateAsyncArchiveFunction, uint32 InFlags = Flags::None, int64 PrimeSize = DefaultPrimeSize);
82 /** Return whether InitializeAsync has completed. If Close is called, state returns to false until the next call to InitializeAsync. */
83 bool IsInitialized() const;
84 /** Wait for InitializeAsync to complete if it is running, otherwise return immediately. */
86
87 // Preloading
88 /** When in PreloadBytes mode, if not already preloading, allocate if necessary the memory for the preloaded bytes and start the chain of asynchronous ReadRequests for the bytes. Returns whether preloading is now active. */
90 /** Cancel any current asynchronous ReadRequests and wait for the asynchronous work to exit. */
92 /** Return whether preloading is in progress. Value may not be up to date since asynchronous work might complete in a race condition. */
93 bool IsPreloading() const;
94 /** When in PreloadBytes mode, allocate if necessary the memory for the preloaded bytes. Return whether the memory is now allocated. */
96 /** Free all memory used by the cache or for preloading (calling StopPreload if necessary). */
98 /** Return whether the cache is currently allocated. */
99 bool IsCacheAllocated() const;
100 /** Return the LowerLevel FArchive if it has been allocated. May return null, even if the FPreloadableFile is currently active. If return value is non-null, caller is responsible for deleting it. */
102
103 // FArchive
104 virtual void Serialize(void* V, int64 Length) final;
105 virtual void Seek(int64 InPos) final;
106 virtual int64 Tell() final;
107 /** Return the size of the file, or -1 if the file does not exist. This is also the amount of memory that will be allocated by AllocateCache. */
108 virtual int64 TotalSize() final;
109 virtual bool Close() final;
110 virtual FString GetArchiveName() const final;
111
112protected:
113 /** Helper function for InitializeAsync, sets up the asynchronous call to InitializeInternal */
114 void InitializeInternalAsync(FCreateArchive&& InCreateArchiveFunction, FCreateAsyncArchive&& InCreateAsyncArchiveFunction, uint32 InFlags, int64 PrimeSize);
115 /** Helper function for InitializeAsync, called from a TaskGraph thread. */
116 void InitializeInternal(FCreateArchive&& InCreateArchiveFunction, FCreateAsyncArchive&& InCreateAsyncArchiveFunction, uint32 Flags, int64 PrimeSize);
118 void SerializeInternal(void* V, int64 Length);
119#endif
123 void OnReadComplete(bool bCanceled, IAsyncReadRequest* ReadRequest);
125 void SerializeSynchronously(void* V, int64 Length);
126
128 /** The Offset into the file or preloaded bytes that will be used in the next call to Serialize. */
129 int64 Pos = 0;
130 /** The number of bytes in the file. */
131 int64 Size = -1;
132
133 /** An Event used for synchronization with asynchronous tasks - InitializingAsync or receiving ReadRequests from the AsynchronousHandle. */
135 /** Threadsafe variable that returns true only after all asynchronous initialization is complete. Is also reset to false when public-interface users call Close(). */
136 TAtomic<bool> bInitialized;
137 /** Threadsafe variable that is true only during the period between initialization until Preloading stops (either due to EOF reached or due to Serialize turning it off. */
138 TAtomic<bool> bIsPreloading;
139 /** Variable that is set to true from the public interface thread to signal that (temporarily) no further ReadRequests should be sent when the currently active one completes. */
140 TAtomic<bool> bIsPreloadingPaused;
141
142 /** An array of bytes of size Size. Non-null only in PreloadBytes mode and in-between calls to AllocateCache/ReleaseCache. */
143 uint8* CacheBytes = nullptr;
144 /**
145 * Number of bytes in CacheBytes that have already been read. This is used in Serialize to check which bytes are available and in preloading to know for which bytes to issue a read request.
146 * This variable is read-only threadsafe. It is guaranteed to be written only after the bytes in CacheBytes have finished writing, and it is guaranteed to be written before bIsPreloading is written.
147 * It is not fully threadsafe; threads that need to write to CacheEnd need to do their Read/Write within the PreloadLock CriticalSection.
148 */
149 TAtomic<int64> CacheEnd;
150
151 /** The handle used for PreloadBytes mode, to fulfull ReadReqeusts. */
153 /** The archive used in PreloadHandle mode or to service Serialize requests that are beyond CacheEnd when in PreloadBytes mode. */
156 /** A duplicate handle used in serialize to validate the returned bytes are correct. */
158#endif
159 /** ReadRequests that have completed but we have not yet deleted. */
161 /** The number of bytes requested from the AsynchronousHandle in each ReadRequest. Larger values have slightly faster results due to per-call overhead, but add greater latency to Serialize calls that read past the end of the cache. */
163
164 /** CriticalSection used to synchronize access to the CacheBytes. */
166 /** Set to true if OnReadComplete is called inline on the same thread from ReadRequest; we need special handling for this case. */
168 /** Set to true during the ReadRequest call to allow us to detect if OnReadComplete is called inline on the same thread from ReadRequest. */
170
171 /** Saved values from the inline OnReadComplete call */
173 {
174 public:
175 void Set(bool bInCanceled, IAsyncReadRequest* InReadRequest)
176 {
177 bCanceled = bInCanceled;
178 ReadRequest = InReadRequest;
179 }
180 void Get(bool& bOutCanceled, IAsyncReadRequest*& OutReadRequest)
181 {
182 bOutCanceled = bCanceled;
183 OutReadRequest = ReadRequest;
184 ReadRequest = nullptr;
185 }
186 private:
187 bool bCanceled = false;
188 IAsyncReadRequest* ReadRequest = nullptr;
189 }
190 SavedReadCompleteArguments;
191
192 friend class FPreloadableFileProxy;
193};
194
195/**
196 * An FPreloadableArchive that is customized for reading files from IFileManager.
197 *
198 * This class also supports registration of instances of this class by filename, which other systems in the engine can use to request
199 * an FArchive for the preload file, if it exists, replacing a call they would otherwise make to IFileManager::Get().CreateFileReader.
200 *
201 * As with the base class, the preloading can work in either PreloadBytes or PreloadHandle mode.
202 *
203 * Activate PreloadBytes mode by passing Flags::PreloadBytes to InitializeAsync.
204 * Activate PreloadHandle mode by passing Flags::PreloadHandle to InitializeAsync, optionally or'd with Flags::Prime.
205 *
206 */
208{
209public:
210 FPreloadableFile(FStringView FileName);
211
212 /** Initialize the FPreloadableFile asynchronously, performing FileOpen operations on another thread. Use IsInitialized or WaitForInitialization to check progress. */
213 void InitializeAsync(uint32 InFlags = Flags::None, int64 PrimeSize=DefaultPrimeSize);
214
215 // Registration
216 /**
217 * Try to register the given FPreloadableFile instance to handle the next call to TryTakeArchive for its FileName.
218 * Will fail if the instance has not been initialized or if another instance has already registered for the Filename.
219 * Return whether the instance is currently registered. Returns true if the instance was already registered.
220 * Registered files are referenced-counted, and the reference will not be dropped until (1) (TryTakeArchive or UnRegister is called) and (2) (PreloadBytes mode only) the archive returned from TryTakeArchive is deleted.
221 */
222 static bool TryRegister(const TSharedPtr<FPreloadableFile>& PreloadableFile);
223 /**
224 * Look up an FPreloadableFile instance registered for the given FileName, and return an FArchive from it.
225 * If found, removes the registration so no future call to TryTakeArchive can sue the same FArchive.
226 * If the instance is in PreloadHandle mode, the Lower-Level FArchive will be detached from the FPreloadableFile and returned using DetachLowerLevel.
227 * If the instance is in PreloadBytes mode, a ProxyArchive will be returned that forwards call to the FPreloadableFile instance.
228 */
229 static FArchive* TryTakeArchive(const TCHAR* FileName);
230 /** Remove the FPreloadableFile instance if it is registered for its FileName. Returns whether the instance was registered. */
231 static bool UnRegister(const TSharedPtr<FPreloadableFile>& PreloadableFile);
232
233private:
234 /** Map used for TryTakeArchive registration. */
236};
237
238/**
239 * A helper class for systems that want to make their own registration system.
240 * A proxy archive that keeps a shared pointer to the inner FPreloadableArchive, so that the inner preloadablearchive will remain
241 * alive until at least as long as the proxy is destroyed.
242 */
244{
245public:
246 explicit FPreloadableArchiveProxy(const TSharedPtr<FPreloadableArchive>& InArchive)
248 {
249 check(Archive);
250 }
251 virtual void Seek(int64 InPos) final
252 {
253 Archive->Seek(InPos);
254 }
255 virtual int64 Tell() final
256 {
257 return Archive->Tell();
258 }
259 virtual int64 TotalSize() final
260 {
261 return Archive->TotalSize();
262 }
263 virtual bool Close() final
264 {
265 return Archive->Close();
266 }
267 virtual void Serialize(void* V, int64 Length) final
268 {
269 Archive->Serialize(V, Length);
270 }
271 virtual FString GetArchiveName() const final
272 {
273 return Archive->GetArchiveName();
274 }
275
276private:
278};
#define check(expr)
#define FPRELOADABLEFILE_TEST_ENABLED
FWindowsCriticalSection FCriticalSection
Definition Event.h:21
TAtomic< int64 > CacheEnd
FCriticalSection PreloadLock
bool IsPreloading() const
virtual bool Close() final
void SetPageSize(int64 PageSize)
TArray< IAsyncReadRequest * > RetiredRequests
TAtomic< bool > bIsPreloadingPaused
FArchive * DetachLowerLevel()
virtual ~FPreloadableArchive()
bool ResumePreloadNonRecursive()
TAtomic< bool > bIsPreloading
void SerializeSynchronously(void *V, int64 Length)
virtual int64 Tell() final
void OnReadComplete(bool bCanceled, IAsyncReadRequest *ReadRequest)
void WaitForInitialization() const
virtual void Serialize(void *V, int64 Length) final
bool IsCacheAllocated() const
void InitializeInternalAsync(FCreateArchive &&InCreateArchiveFunction, FCreateAsyncArchive &&InCreateAsyncArchiveFunction, uint32 InFlags, int64 PrimeSize)
bool IsInitialized() const
void InitializeInternal(FCreateArchive &&InCreateArchiveFunction, FCreateAsyncArchive &&InCreateAsyncArchiveFunction, uint32 Flags, int64 PrimeSize)
virtual FString GetArchiveName() const final
TUniqueFunction< IAsyncReadFileHandle *()> FCreateAsyncArchive
FPreloadableArchive(FStringView ArchiveName)
TUniquePtr< FArchive > SynchronousArchive
virtual void Seek(int64 InPos) final
TUniqueFunction< FArchive *()> FCreateArchive
TAtomic< bool > bInitialized
TUniquePtr< IAsyncReadFileHandle > AsynchronousHandle
void InitializeAsync(FCreateAsyncArchive &&InCreateAsyncArchiveFunction, uint32 InFlags=Flags::None, int64 PrimeSize=DefaultPrimeSize)
void InitializeAsync(FCreateArchive &&InCreateArchiveFunction, uint32 InFlags=Flags::None, int64 PrimeSize=DefaultPrimeSize)
virtual int64 TotalSize() final
virtual int64 Tell() final
virtual void Serialize(void *V, int64 Length) final
TSharedPtr< FPreloadableArchive > Archive
FPreloadableArchiveProxy(const TSharedPtr< FPreloadableArchive > &InArchive)
virtual bool Close() final
virtual FString GetArchiveName() const final
virtual void Seek(int64 InPos) final
virtual int64 TotalSize() final
FPreloadableFile(FStringView FileName)
static FArchive * TryTakeArchive(const TCHAR *FileName)
static bool TryRegister(const TSharedPtr< FPreloadableFile > &PreloadableFile)
static TMap< FString, TSharedPtr< FPreloadableFile > > RegisteredFiles
void InitializeAsync(uint32 InFlags=Flags::None, int64 PrimeSize=DefaultPrimeSize)
static bool UnRegister(const TSharedPtr< FPreloadableFile > &PreloadableFile)
void Set(bool bInCanceled, IAsyncReadRequest *InReadRequest)
void Get(bool &bOutCanceled, IAsyncReadRequest *&OutReadRequest)