153 lines
6.3 KiB
C++
153 lines
6.3 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "CoreTypes.h"
|
|
|
|
#include "Async/Future.h"
|
|
#include "Containers/Array.h"
|
|
#include "Containers/StringView.h"
|
|
#include "HAL/Platform.h"
|
|
#include "Misc/AssertionMacros.h"
|
|
|
|
#ifndef WITH_ASYNCIODELETE_DEBUG
|
|
#if DO_CHECK
|
|
#define WITH_ASYNCIODELETE_DEBUG 1 // When enabled the class uses extra memory and does some tracking
|
|
#else
|
|
#define WITH_ASYNCIODELETE_DEBUG 0
|
|
#endif
|
|
#endif
|
|
|
|
class FEvent;
|
|
|
|
/**
|
|
* Helper class to asynchronously delete paths (files or directories).
|
|
* The deleted paths are moved to a unique temporary location immediately, and then asynchronously deleted later.
|
|
* Note that two FAsyncIODeletes (in the same process or in multiple processes) at the same with the same InOwnedTempRoot
|
|
* is forbidden and will cause behavior errors. Caller must ensure the machine-global uniqueness of InOwnedTempRoot.
|
|
*
|
|
* FAsyncIODelete's public interface is not threadsafe; all calls to the public interface must be run from the same thread.
|
|
*/
|
|
class FAsyncIODelete
|
|
{
|
|
public:
|
|
/**
|
|
* Quick constructor to create flags; does not take any IO action until Setup or a Delete function is used
|
|
*
|
|
* @param InOwnedTempRoot - The FAsyncIODelete system takes ownership of this directory, and deletes it when done.
|
|
* See SetTempRoot. The FAsyncIODelete will not be able to delete directories until a valid TempRoot has been set,
|
|
* either in the constructor or in SetTempRoot
|
|
*/
|
|
explicit FAsyncIODelete(const FStringView& InOwnedTempRoot=FStringView());
|
|
|
|
/* Synchronously waits for all requested deletions to complete */
|
|
~FAsyncIODelete();
|
|
|
|
/**
|
|
* Set a TempRoot directory to hold deleted directories from elsewhere, which is potentially shared with other
|
|
* processes running other FAsyncIODelete. The AsyncIoDelete processes take ownership of this directory and will
|
|
* delete it; do not pass in a directory shared with other systems, create a subdirectory for it.
|
|
* Must not be a parent or child of any other FAsyncDelete's TempRoot, but is allowed to be the same.
|
|
* Each new FAsyncIODelete process will negotiate with other proccesses using lock files, to delete the directory
|
|
* when all processes are done.
|
|
* If the FAsyncIODelete has already been used to delete files, this Set will block until all deletes are finished.
|
|
*/
|
|
void SetTempRoot(FStringView InSharedRoot);
|
|
FStringView GetTempRoot() const { return SharedTempRoot; }
|
|
/**
|
|
* Returns the path this AsyncIODelete is using for its uniquely owned deletion root (a subdirectory of TempRoot).
|
|
* Will return empty before Setup (or on-demand Setup) or after Teardown.
|
|
*/
|
|
FStringView GetDeletionRoot() const { return TempRoot; };
|
|
|
|
/**
|
|
* Set whether new background deletes are paused. If paused, paths will be moved immediately but will not be deleted from the temporary location until unpaused (or at the FAsyncIODelete's destruction).
|
|
* Setting deletes paused does not affect deletes already in progress.
|
|
*/
|
|
void SetDeletesPaused(bool bInPaused);
|
|
bool GetDeletesPaused() const { return bPaused; }
|
|
|
|
/**
|
|
* Prepare the directory on disk. Called OnDemand from the other interface functions. Can also be called if desired by client code.
|
|
* May take a long time to run if cleanup from a crashed previous process is required.
|
|
*/
|
|
void Setup();
|
|
|
|
/** Synchronously wait for all tasks to complete, and remove the temproot. */
|
|
void Teardown();
|
|
|
|
/**
|
|
* Asynchronously delete the given directory (and all subdirs). The directory is moved and no longer exists before returning, but the move location will be deleted from disk later.
|
|
* The delete will fail if the given path is not a directory.
|
|
* The delete will fail and return false if the given path is a parent directory, child directory, or is equal to TempRoot.
|
|
* The delete can also fail due to IO Errors - insufficient permissions, device failure.
|
|
* If the given path is on a different volume than temp root, or the move fails for other reasons, the delete will proceed synchronously but will succeed.
|
|
* If the given path does not exist, DeleteDirectory will return true.
|
|
*
|
|
* @param PathToDelete - If DeleteDirectory returns true, this path has been removed from disk.
|
|
*
|
|
* @return Whether the requested path is no longer present on disk.
|
|
*/
|
|
bool DeleteDirectory(const FStringView& PathToDelete)
|
|
{
|
|
return Delete(PathToDelete, EPathType::Directory);
|
|
}
|
|
|
|
/** Asynchronously delete a file. Behavior is otherwise the same as DeleteDirectory. */
|
|
bool DeleteFile(const FStringView& PathToDelete)
|
|
{
|
|
return Delete(PathToDelete, EPathType::File);
|
|
}
|
|
|
|
/** Synchronously wait for all deletes to complete. */
|
|
bool WaitForAllTasks(float TimeLimitSeconds = 0.0f);
|
|
|
|
static bool AsyncEnabled();
|
|
|
|
/** Internal accessors for unittests */
|
|
static FStringView GetLockSuffix();
|
|
static void SetMaxWaitSecondsForLock(float WaitSeconds = -1.f);
|
|
|
|
private:
|
|
/** Update the ActiveTaskCount and events for a task completing */
|
|
void OnTaskComplete();
|
|
|
|
enum class EPathType
|
|
{
|
|
File,
|
|
Directory
|
|
};
|
|
struct FDeleteRequest
|
|
{
|
|
FString Path;
|
|
EPathType PathType;
|
|
};
|
|
/** Asynchronously delete a file or directory. We handle both in the same function, but we want the interface to be explicit. */
|
|
bool Delete(const FStringView& PathToDelete, EPathType ExpectedType);
|
|
|
|
/** Create and store the task to delete the given Directory or File */
|
|
void CreateDeleteTask(const FStringView& InDeletePath, EPathType PathType);
|
|
|
|
/** Delete the given path synchronously; called from a task or in error fallback cases from the public thread */
|
|
bool SynchronousDelete(const TCHAR* InDeletePath, EPathType PathType);
|
|
|
|
bool TryPurgeOldAndCreateRoot(bool bCreateRoot, TArray<FDeleteRequest>& OutHangingRootsToDelete);
|
|
|
|
FString SharedTempRoot;
|
|
FString TempRoot;
|
|
TUniquePtr<FArchive> TempRootLockFile;
|
|
TArray<FString> PausedDeletes;
|
|
FCriticalSection CriticalSection; // We use a CriticalSection instead of a TAtomic ActiveTaskCount so that we can atomically { trigger TasksComplete if ActiveTaskCount == 0 }
|
|
FEvent* TasksComplete = nullptr;
|
|
uint32 ActiveTaskCount = 0;
|
|
uint32 DeleteCounter = 0;
|
|
bool bInitialized = false;
|
|
bool bAsyncInitialized = false;
|
|
bool bPaused = false;
|
|
|
|
#if WITH_ASYNCIODELETE_DEBUG
|
|
static TArray<FString> AllTempRoots;
|
|
static void AddTempRoot(const FStringView& InTempRoot);
|
|
static void RemoveTempRoot(const FStringView& InTempRoot);
|
|
#endif
|
|
}; |