Closing technical debt
- Manage resources deallocations in AssetManager - Reformatting in AssetManager - Fix resource address synchronization in AssetManager - Windows native file system uses IOCP for performance
This commit is contained in:
parent
250a1641ad
commit
4815fb4e1e
@ -28,11 +28,11 @@ export
|
|||||||
ee_assets::AssetType type = ee_assets::BINARY;
|
ee_assets::AssetType type = ee_assets::BINARY;
|
||||||
|
|
||||||
std::atomic<bool> isReady;
|
std::atomic<bool> isReady;
|
||||||
AssetAddress *resourceAddress;
|
std::atomic<AssetAddress*> resourceAddress;
|
||||||
|
|
||||||
explicit AssetInfo(void *resource) {
|
explicit AssetInfo(void *resource) {
|
||||||
this->resourceAddress = Allocate<AssetAddress>();
|
this->resourceAddress = Allocate<AssetAddress>();
|
||||||
this->resourceAddress->resource.store(resource);
|
this->resourceAddress.load(std::memory_order_relaxed)->resource.store(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
AssetInfo(const AssetInfo&) = delete;
|
AssetInfo(const AssetInfo&) = delete;
|
||||||
@ -40,7 +40,7 @@ export
|
|||||||
|
|
||||||
AssetInfo(AssetInfo&& other) noexcept :
|
AssetInfo(AssetInfo&& other) noexcept :
|
||||||
type(other.type),
|
type(other.type),
|
||||||
resourceAddress(other.resourceAddress) {
|
resourceAddress(other.resourceAddress.load(std::memory_order_relaxed)) {
|
||||||
isReady.store(other.isReady.load(std::memory_order_relaxed), std::memory_order_relaxed);
|
isReady.store(other.isReady.load(std::memory_order_relaxed), std::memory_order_relaxed);
|
||||||
|
|
||||||
other.resourceAddress = nullptr;
|
other.resourceAddress = nullptr;
|
||||||
@ -50,7 +50,7 @@ export
|
|||||||
AssetInfo& operator=(AssetInfo&& other) noexcept {
|
AssetInfo& operator=(AssetInfo&& other) noexcept {
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
type = other.type;
|
type = other.type;
|
||||||
resourceAddress = other.resourceAddress;
|
resourceAddress = other.resourceAddress.load(std::memory_order_relaxed);
|
||||||
isReady.store(other.isReady.load(std::memory_order_relaxed), std::memory_order_relaxed);
|
isReady.store(other.isReady.load(std::memory_order_relaxed), std::memory_order_relaxed);
|
||||||
|
|
||||||
other.resourceAddress = nullptr;
|
other.resourceAddress = nullptr;
|
||||||
@ -105,7 +105,7 @@ export
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadAsync(ee_assets::AssetType type, const std::filesystem::path& path) {
|
void LoadAsync(const ee_assets::AssetType type, const std::filesystem::path& path) {
|
||||||
AssetStreamCommand command;
|
AssetStreamCommand command;
|
||||||
command.type = AssetStreamCommand::LOAD;
|
command.type = AssetStreamCommand::LOAD;
|
||||||
command.targetPath = path;
|
command.targetPath = path;
|
||||||
@ -150,21 +150,22 @@ export
|
|||||||
return Get<T>(Find(id));
|
return Get<T>(Find(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Redirect(const AssetHandle& handle, void *resource) const {
|
// ReSharper disable once CppMemberFunctionMayBeConst
|
||||||
|
void Redirect(const AssetHandle& handle, void *resource) {
|
||||||
const AssetInfo& asset = activeAssets[handle.slot];
|
const AssetInfo& asset = activeAssets[handle.slot];
|
||||||
if (asset.resourceAddress == nullptr) {
|
AssetAddress *address = asset.resourceAddress.load(std::memory_order_relaxed);
|
||||||
|
if (address == nullptr) {
|
||||||
LOG(WARNING, "Attempt to redirect empty handle");
|
LOG(WARNING, "Attempt to redirect empty handle");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: LEAK! Free old resource after redirecting handler to other resource
|
if (
|
||||||
if (asset.resourceAddress->resource.load() != nullptr) {
|
void *oldResource = address->resource.exchange(
|
||||||
void *oldResource = asset.resourceAddress->resource.exchange(nullptr, std::memory_order_acq_rel);
|
resource,
|
||||||
//if (oldResource)
|
std::memory_order_acq_rel
|
||||||
//Deallocate(oldResource);
|
)
|
||||||
}
|
)
|
||||||
|
Deallocate(oldResource);
|
||||||
asset.resourceAddress->resource.store(resource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returned resource reference should not be stored
|
// Returned resource reference should not be stored
|
||||||
@ -184,7 +185,7 @@ export
|
|||||||
void Free(const AssetHandle& handle) {
|
void Free(const AssetHandle& handle) {
|
||||||
AssetInfo& asset = activeAssets[handle.slot];
|
AssetInfo& asset = activeAssets[handle.slot];
|
||||||
Redirect(handle, nullptr);
|
Redirect(handle, nullptr);
|
||||||
Deallocate(asset.resourceAddress);
|
Deallocate(asset.resourceAddress.load(std::memory_order_relaxed));
|
||||||
asset.resourceAddress = nullptr;
|
asset.resourceAddress = nullptr;
|
||||||
|
|
||||||
freeAssetSlots.push_back(handle.slot);
|
freeAssetSlots.push_back(handle.slot);
|
||||||
@ -192,57 +193,24 @@ export
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void Update() override {
|
void Update() override {
|
||||||
// TODO: FinishLoadTasks()
|
FinishLoadTasks();
|
||||||
for (AssetLoadingEntry& entry : loadingEntries) {
|
|
||||||
if (entry.fileOwner->PollReady(*entry.file)) {
|
|
||||||
AssetInfo& asset = activeAssets[entry.handle.slot];
|
|
||||||
|
|
||||||
switch (asset.type) {
|
|
||||||
case ee_assets::MAP_MESH:
|
|
||||||
FinishLoadCapnprotoDeserialize(entry, mapMeshesStorage);
|
|
||||||
break;
|
|
||||||
case ee_assets::SAVE:
|
|
||||||
FinishLoadCapnprotoDeserialize(entry, savesStorage);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ee_assets::SHADER:
|
|
||||||
FinishLoadConstruct(entry, shadersStorage);
|
|
||||||
break;
|
|
||||||
case ee_assets::MATERIAL:
|
|
||||||
FinishLoadCapnprotoDeserialize(entry, materialsStorage);
|
|
||||||
break;
|
|
||||||
case ee_assets::IMAGE:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ee_assets::BINARY:
|
|
||||||
default:
|
|
||||||
FinishLoadConstruct(entry, binaryStorage);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.fileOwner->Close(*entry.file);
|
|
||||||
#ifndef NDEBUG
|
|
||||||
LOG(INFO, "Asset loaded: [%hs]", entry.file->debugPath.generic_u8string().c_str());
|
|
||||||
#endif
|
|
||||||
entry.file = nullptr;
|
|
||||||
asset.isReady.store(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::erase_if(loadingEntries, [](const AssetLoadingEntry& entry) {
|
|
||||||
return entry.file == nullptr;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Release() override {
|
void Release() override {
|
||||||
for (const AssetInfo& asset : activeAssets) {
|
for (const AssetInfo& asset : activeAssets) {
|
||||||
//TODO: LEAK! Free resource
|
if (
|
||||||
if (asset.resourceAddress->resource.load() != nullptr) {
|
AssetAddress* address = asset.resourceAddress.load(std::memory_order_relaxed);
|
||||||
void *resource = asset.resourceAddress->resource.exchange(nullptr, std::memory_order_acq_rel);
|
address != nullptr
|
||||||
//if (resource)
|
) {
|
||||||
//Deallocate(resource);
|
if (
|
||||||
|
void *resource = address->resource.exchange(
|
||||||
|
nullptr,
|
||||||
|
std::memory_order_acq_rel
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Deallocate(resource);
|
||||||
|
Deallocate(address);
|
||||||
}
|
}
|
||||||
Deallocate(asset.resourceAddress);
|
|
||||||
}
|
}
|
||||||
activeAssets.clear();
|
activeAssets.clear();
|
||||||
|
|
||||||
@ -289,7 +257,7 @@ export
|
|||||||
std::size_t assetPos = 0;
|
std::size_t assetPos = 0;
|
||||||
if (freeAssetSlots.size() > 0) {
|
if (freeAssetSlots.size() > 0) {
|
||||||
assetPos = freeAssetSlots.back();
|
assetPos = freeAssetSlots.back();
|
||||||
Deallocate(activeAssets[assetPos].resourceAddress);
|
Deallocate(activeAssets[assetPos].resourceAddress.load(std::memory_order_relaxed));
|
||||||
activeAssets[assetPos] = std::move(asset);
|
activeAssets[assetPos] = std::move(asset);
|
||||||
freeAssetSlots.pop_back();
|
freeAssetSlots.pop_back();
|
||||||
}
|
}
|
||||||
@ -342,11 +310,57 @@ export
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (command.out != nullptr)
|
if (command.out != nullptr)
|
||||||
command.out->store(activeAssets[command.target.slot].resourceAddress->resource);
|
command.out->store(
|
||||||
|
activeAssets[command.target.slot].resourceAddress.load(std::memory_order_relaxed)->resource
|
||||||
|
);
|
||||||
|
|
||||||
command.type = AssetStreamCommand::COMPLETED;
|
command.type = AssetStreamCommand::COMPLETED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FinishLoadTasks() {
|
||||||
|
diskSystem->PollLoads();
|
||||||
|
|
||||||
|
for (AssetLoadingEntry& entry : loadingEntries) {
|
||||||
|
if (entry.file->isCompleted) {
|
||||||
|
AssetInfo& asset = activeAssets[entry.handle.slot];
|
||||||
|
|
||||||
|
switch (asset.type) {
|
||||||
|
case ee_assets::MAP_MESH:
|
||||||
|
FinishLoadCapnprotoDeserialize(entry, mapMeshesStorage);
|
||||||
|
break;
|
||||||
|
case ee_assets::SAVE:
|
||||||
|
FinishLoadCapnprotoDeserialize(entry, savesStorage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ee_assets::SHADER:
|
||||||
|
FinishLoadConstruct(entry, shadersStorage);
|
||||||
|
break;
|
||||||
|
case ee_assets::MATERIAL:
|
||||||
|
FinishLoadCapnprotoDeserialize(entry, materialsStorage);
|
||||||
|
break;
|
||||||
|
case ee_assets::IMAGE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ee_assets::BINARY:
|
||||||
|
default:
|
||||||
|
FinishLoadConstruct(entry, binaryStorage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.fileOwner->Close(*entry.file);
|
||||||
|
#ifndef NDEBUG
|
||||||
|
LOG(INFO, "Asset loaded: [%hs]", entry.file->debugPath.generic_u8string().c_str());
|
||||||
|
#endif
|
||||||
|
entry.file = nullptr;
|
||||||
|
asset.isReady.store(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::erase_if(loadingEntries, [](const AssetLoadingEntry& entry) {
|
||||||
|
return entry.file == nullptr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
template <ee_assets::Asset T>
|
template <ee_assets::Asset T>
|
||||||
void FinishLoadConstruct(AssetLoadingEntry& loaded, std::vector<T*>& storage) {
|
void FinishLoadConstruct(AssetLoadingEntry& loaded, std::vector<T*>& storage) {
|
||||||
T *resource = Allocate<T>(loaded.file->data, loaded.file->size);
|
T *resource = Allocate<T>(loaded.file->data, loaded.file->size);
|
||||||
|
|||||||
@ -15,19 +15,30 @@ export
|
|||||||
std::uint8_t *data = nullptr;
|
std::uint8_t *data = nullptr;
|
||||||
std::uint8_t *buffer = nullptr;
|
std::uint8_t *buffer = nullptr;
|
||||||
|
|
||||||
|
bool isCompleted = false;
|
||||||
|
bool isClosing = false;
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
std::filesystem::path debugPath;
|
std::filesystem::path debugPath;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
HANDLE win32Handle = INVALID_HANDLE_VALUE;
|
HANDLE win32Handle = INVALID_HANDLE_VALUE;
|
||||||
OVERLAPPED overlapped;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
struct IOContext {
|
||||||
|
OVERLAPPED overlapped;
|
||||||
|
FileHandle *file;
|
||||||
|
std::size_t currentOffset;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
class IFileSystem {
|
class IFileSystem {
|
||||||
public:
|
public:
|
||||||
static constexpr std::uint32_t FILE_OPEN_NONE = 0;
|
static constexpr std::uint32_t FILE_OPEN_NONE = 0;
|
||||||
@ -41,7 +52,7 @@ export
|
|||||||
|
|
||||||
virtual void ReadAsync(FileHandle& file, std::uint32_t bufferSize, std::uint32_t flags) = 0;
|
virtual void ReadAsync(FileHandle& file, std::uint32_t bufferSize, std::uint32_t flags) = 0;
|
||||||
|
|
||||||
virtual bool PollReady(FileHandle& file) = 0;
|
virtual void PollLoads() = 0;
|
||||||
|
|
||||||
virtual void Close(FileHandle& file) = 0;
|
virtual void Close(FileHandle& file) = 0;
|
||||||
|
|
||||||
@ -55,12 +66,26 @@ export
|
|||||||
|
|
||||||
class NativeFileSystem final : public IFileSystem {
|
class NativeFileSystem final : public IFileSystem {
|
||||||
public:
|
public:
|
||||||
FileHandle& Open(const std::filesystem::path& path, std::uint32_t flags) override;
|
NativeFileSystem();
|
||||||
|
~NativeFileSystem() override;
|
||||||
|
|
||||||
void ReadAsync(FileHandle& file, std::uint32_t bufferSize, std::uint32_t flags) override;
|
FileHandle& Open(const std::filesystem::path &path, std::uint32_t flags) override;
|
||||||
|
|
||||||
bool PollReady(FileHandle& file) override;
|
void ReadAsync(FileHandle &file, std::uint32_t bufferSize, std::uint32_t flags) override;
|
||||||
|
|
||||||
void Close(FileHandle& file) override;
|
void PollLoads() override;
|
||||||
|
|
||||||
|
void Close(FileHandle &file) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE iocpHandle = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
bool HandleChunk(IOContext *context, DWORD bytesTransferred, bool success);
|
||||||
|
void IssueRead(IOContext *context);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void FreeFile(FileHandle &file);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,8 +5,6 @@ module;
|
|||||||
module FileSystem;
|
module FileSystem;
|
||||||
import utils;
|
import utils;
|
||||||
|
|
||||||
//TOO: REWORK WITH IOCP
|
|
||||||
|
|
||||||
IFileSystem::~IFileSystem() {
|
IFileSystem::~IFileSystem() {
|
||||||
for (FileHandle *handle : activeFiles)
|
for (FileHandle *handle : activeFiles)
|
||||||
Deallocate(handle);
|
Deallocate(handle);
|
||||||
@ -16,6 +14,27 @@ IFileSystem::~IFileSystem() {
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
NativeFileSystem::NativeFileSystem() {
|
||||||
|
iocpHandle = CreateIoCompletionPort(
|
||||||
|
INVALID_HANDLE_VALUE,
|
||||||
|
nullptr,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
if (iocpHandle == nullptr) {
|
||||||
|
LOG(CRITICAL, "Failed to create IOCP. Error: %d", GetLastError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeFileSystem::~NativeFileSystem() {
|
||||||
|
// I have no idea why ReSharper think that this condition is always false
|
||||||
|
// ReSharper disable once CppDFAConstantConditions
|
||||||
|
if (iocpHandle != INVALID_HANDLE_VALUE && iocpHandle != nullptr) {
|
||||||
|
// ReSharper disable once CppDFAUnreachableCode
|
||||||
|
CloseHandle(iocpHandle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FileHandle& NativeFileSystem::Open(const std::filesystem::path& path, const std::uint32_t flags) {
|
FileHandle& NativeFileSystem::Open(const std::filesystem::path& path, const std::uint32_t flags) {
|
||||||
FileHandle *result = Allocate<FileHandle>();
|
FileHandle *result = Allocate<FileHandle>();
|
||||||
|
|
||||||
@ -30,14 +49,14 @@ FileHandle& NativeFileSystem::Open(const std::filesystem::path& path, const std:
|
|||||||
if (flags & FILE_OPEN_WRITE)
|
if (flags & FILE_OPEN_WRITE)
|
||||||
createFileMode |= GENERIC_WRITE;
|
createFileMode |= GENERIC_WRITE;
|
||||||
|
|
||||||
HANDLE file = CreateFileW(
|
const HANDLE file = CreateFileW(
|
||||||
path.c_str(),
|
path.c_str(),
|
||||||
createFileMode,
|
createFileMode,
|
||||||
NULL,
|
NULL,
|
||||||
NULL,
|
nullptr,
|
||||||
OPEN_EXISTING,
|
OPEN_EXISTING,
|
||||||
FILE_FLAG_OVERLAPPED,
|
FILE_FLAG_OVERLAPPED,
|
||||||
NULL
|
nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
if (file == INVALID_HANDLE_VALUE) {
|
if (file == INVALID_HANDLE_VALUE) {
|
||||||
@ -47,12 +66,23 @@ FileHandle& NativeFileSystem::Open(const std::filesystem::path& path, const std:
|
|||||||
}
|
}
|
||||||
|
|
||||||
result->win32Handle = file;
|
result->win32Handle = file;
|
||||||
result->size = GetFileSize(file, NULL);
|
result->size = GetFileSize(file, nullptr);
|
||||||
if (result->size == INVALID_FILE_SIZE)
|
if (result->size == INVALID_FILE_SIZE)
|
||||||
LOG(ERROR, "Failed to get size of file %s. Error: %d", path.c_str(), GetLastError());
|
LOG(ERROR, "Failed to get size of file %s. Error: %d", path.c_str(), GetLastError());
|
||||||
|
|
||||||
|
if (
|
||||||
|
CreateIoCompletionPort(
|
||||||
|
file,
|
||||||
|
iocpHandle,
|
||||||
|
NULL,
|
||||||
|
NULL
|
||||||
|
) == nullptr
|
||||||
|
) {
|
||||||
|
LOG(ERROR, "Failed to associate file with IOCP. Error: %d", GetLastError());
|
||||||
|
}
|
||||||
|
|
||||||
if (freeFileSlots.size() > 0) {
|
if (freeFileSlots.size() > 0) {
|
||||||
std::size_t pos = freeFileSlots.back();
|
const std::size_t pos = freeFileSlots.back();
|
||||||
result->id = pos;
|
result->id = pos;
|
||||||
activeFiles[pos] = result;
|
activeFiles[pos] = result;
|
||||||
freeFileSlots.pop_back();
|
freeFileSlots.pop_back();
|
||||||
@ -66,73 +96,140 @@ FileHandle& NativeFileSystem::Open(const std::filesystem::path& path, const std:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NativeFileSystem::ReadAsync(FileHandle& file, const std::uint32_t bufferSize, const std::uint32_t flags) {
|
void NativeFileSystem::ReadAsync(FileHandle& file, const std::uint32_t bufferSize, const std::uint32_t flags) {
|
||||||
file.overlapped = {
|
file.isCompleted = false;
|
||||||
.hEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)
|
file.isClosing = false;
|
||||||
};
|
|
||||||
file.buffer = AllocateArray<std::uint8_t>(bufferSize);
|
|
||||||
file.bufferSize = bufferSize;
|
file.bufferSize = bufferSize;
|
||||||
|
file.buffer = AllocateArray<std::uint8_t>(file.bufferSize);
|
||||||
|
|
||||||
BOOL isSync = ReadFile(
|
IOContext *context = Allocate<IOContext>();
|
||||||
file.win32Handle,
|
context->file = &file;
|
||||||
file.buffer,
|
context->currentOffset = 0;
|
||||||
bufferSize,
|
context->overlapped = {};
|
||||||
NULL,
|
|
||||||
&file.overlapped
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isSync || GetLastError() != ERROR_IO_PENDING)
|
if (!ReadFile(file.win32Handle, file.buffer, bufferSize, nullptr, &context->overlapped)) {
|
||||||
LOG(ERROR, "File reading is synchronious while asynchronious is awaited");
|
if (DWORD error = GetLastError(); error != ERROR_IO_PENDING) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
LOG(ERROR, "Failed to start reading file [%s]. Error: %d", file.debugPath.c_str(), error);
|
||||||
|
#else
|
||||||
|
LOG(ERROR, "Failed to start reading file [%d]. Error: %d", file.id, error);
|
||||||
|
#endif
|
||||||
|
HandleChunk(context, 0, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NativeFileSystem::PollReady(FileHandle& file) {
|
void NativeFileSystem::PollLoads() {
|
||||||
DWORD status = WaitForSingleObject(file.overlapped.hEvent, 0);
|
DWORD bytesTransferred = 0;
|
||||||
|
OVERLAPPED *overlapped = nullptr;
|
||||||
|
ULONG_PTR ck = 999;
|
||||||
|
|
||||||
switch (status) {
|
while (true) {
|
||||||
case WAIT_OBJECT_0:
|
const bool success = GetQueuedCompletionStatus(
|
||||||
{
|
iocpHandle,
|
||||||
if (file.size == file.bufferSize) {
|
&bytesTransferred,
|
||||||
|
&ck,
|
||||||
|
&overlapped,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!overlapped) { // No more ready files
|
||||||
|
if (DWORD error = GetLastError(); !success && error != WAIT_TIMEOUT) {
|
||||||
|
LOG(ERROR, "Can not get files loading status. Error: %d", error);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
IOContext *context = reinterpret_cast<IOContext*>(overlapped);
|
||||||
|
HandleChunk(context, bytesTransferred, success)
|
||||||
|
) {
|
||||||
|
IssueRead(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NativeFileSystem::Close(FileHandle& file) {
|
||||||
|
if (file.win32Handle != INVALID_HANDLE_VALUE) {
|
||||||
|
CancelIoEx(file.win32Handle, nullptr);
|
||||||
|
|
||||||
|
if (file.isCompleted) {
|
||||||
|
FreeFile(file);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
file.isClosing = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NativeFileSystem::HandleChunk(IOContext *context, const DWORD bytesTransferred, const bool success) {
|
||||||
|
FileHandle &file = *context->file;
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
if (const DWORD error = GetLastError(); error != ERROR_OPERATION_ABORTED) {
|
||||||
|
#ifndef NDEBUG
|
||||||
|
LOG(ERROR, "IOCP read error [%s]. Error: %d", file.debugPath.c_str(), error);
|
||||||
|
#else
|
||||||
|
LOG(ERROR, "IOCP read error [%d]. Error: %d", file.id, error);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.isClosing) FreeFile(file);
|
||||||
|
|
||||||
|
Deallocate(context);
|
||||||
|
file.isCompleted = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file.data == nullptr) { // Initialize for first chunk
|
||||||
|
if (file.size <= file.bufferSize) { // File of one chunk, just target resource
|
||||||
file.data = file.buffer;
|
file.data = file.buffer;
|
||||||
file.buffer = nullptr;
|
file.buffer = nullptr;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* TODO: reserve size for data, shift and move */
|
file.data = AllocateArray<std::uint8_t>(file.size); // Allocate memory for all chunks
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
if (bytesTransferred == 0) { // Case of unexpected EOF
|
||||||
}
|
Deallocate(context);
|
||||||
|
if (file.isClosing) FreeFile(file);
|
||||||
case WAIT_ABANDONED:
|
file.isCompleted = true;
|
||||||
case WAIT_TIMEOUT:
|
|
||||||
return false;
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
LOG(CRITICAL, "Failed to get file status. Error: %d", GetLastError());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (file.buffer != nullptr) {
|
||||||
|
std::ranges::copy(file.buffer, file.buffer + bytesTransferred, file.data + context->currentOffset);
|
||||||
|
}
|
||||||
|
context->currentOffset += bytesTransferred;
|
||||||
|
|
||||||
|
if (context->currentOffset >= file.size) { // Finish reading of completed file
|
||||||
|
Deallocate(context);
|
||||||
|
if (file.isClosing) FreeFile(file);
|
||||||
|
file.isCompleted = true;
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NativeFileSystem::Close(FileHandle& file) {
|
void NativeFileSystem::IssueRead(IOContext *context) {
|
||||||
if (file.overlapped.hEvent)
|
// 64-bit offset is seperated for WinAPI
|
||||||
CloseHandle(file.overlapped.hEvent);
|
context->overlapped.Offset = static_cast<DWORD>(context->currentOffset);
|
||||||
|
context->overlapped.OffsetHigh = static_cast<DWORD>(context->currentOffset >> 32);
|
||||||
|
|
||||||
if (file.buffer != nullptr) {
|
if (
|
||||||
DeallocateArray(file.buffer);
|
!ReadFile(
|
||||||
file.buffer = nullptr;
|
context->file->win32Handle,
|
||||||
|
context->file->buffer,
|
||||||
|
context->file->bufferSize,
|
||||||
|
nullptr,
|
||||||
|
&context->overlapped
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
if (GetLastError() != ERROR_IO_PENDING) {
|
||||||
|
HandleChunk(context, 0, false);
|
||||||
}
|
}
|
||||||
if (file.data != nullptr) {
|
|
||||||
DeallocateArray(file.data);
|
|
||||||
file.data = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file.win32Handle != INVALID_HANDLE_VALUE) {
|
|
||||||
CloseHandle(file.win32Handle);
|
|
||||||
file.win32Handle = INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
freeFileSlots.push_back(file.id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -141,3 +238,17 @@ void NativeFileSystem::Close(FileHandle& file) {
|
|||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void NativeFileSystem::FreeFile(FileHandle &file) {
|
||||||
|
if (file.buffer != nullptr) DeallocateArray(file.buffer);
|
||||||
|
if (file.data != nullptr) DeallocateArray(file.data);
|
||||||
|
file.buffer = nullptr;
|
||||||
|
file.data = nullptr;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (file.win32Handle != INVALID_HANDLE_VALUE) CloseHandle(file.win32Handle);
|
||||||
|
file.win32Handle = INVALID_HANDLE_VALUE;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
freeFileSlots.push_back(file.id);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user