89 lines
2.8 KiB
C++
89 lines
2.8 KiB
C++
#define NOMINMAX
|
|
#include "io/hdf5/hdf5_atomic_file.h"
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <atomic>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <filesystem>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "io/hdf5/hdf5_primitives.h"
|
|
#include "io/hdf5/hdf5_raii.h"
|
|
|
|
namespace fesa::hdf5_internal {
|
|
namespace {
|
|
|
|
bool AtomicReplace(const std::filesystem::path& candidate_path,
|
|
const std::filesystem::path& output_path) {
|
|
const DWORD attributes = GetFileAttributesW(output_path.c_str());
|
|
if (attributes != INVALID_FILE_ATTRIBUTES) {
|
|
return ReplaceFileW(output_path.c_str(), candidate_path.c_str(), nullptr,
|
|
0U, nullptr, nullptr) != FALSE;
|
|
}
|
|
const DWORD error = GetLastError();
|
|
if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) {
|
|
return false;
|
|
}
|
|
return MoveFileExW(candidate_path.c_str(), output_path.c_str(),
|
|
MOVEFILE_WRITE_THROUGH) != FALSE;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
CandidateFileGuard::CandidateFileGuard(std::filesystem::path path)
|
|
: path_{std::move(path)} {}
|
|
|
|
CandidateFileGuard::~CandidateFileGuard() {
|
|
if (active_) {
|
|
std::error_code ignored;
|
|
(void)std::filesystem::remove(path_, ignored);
|
|
}
|
|
}
|
|
|
|
void CandidateFileGuard::Release() noexcept { active_ = false; }
|
|
|
|
std::filesystem::path MakeCandidatePath(
|
|
const std::filesystem::path& output_path) {
|
|
static std::atomic<std::uint64_t> sequence{0U};
|
|
const std::filesystem::path parent = output_path.parent_path();
|
|
for (std::size_t attempt = 0U; attempt < 1024U; ++attempt) {
|
|
const std::wstring filename =
|
|
L"." + output_path.filename().wstring() + L".tmp." +
|
|
std::to_wstring(GetCurrentProcessId()) + L"." +
|
|
std::to_wstring(sequence.fetch_add(1U));
|
|
const std::filesystem::path candidate = parent / filename;
|
|
std::error_code error;
|
|
const bool exists = std::filesystem::exists(candidate, error);
|
|
if (error) {
|
|
throw Hdf5Failure{"Unable to inspect the temporary output path."};
|
|
}
|
|
if (!exists) {
|
|
return candidate;
|
|
}
|
|
}
|
|
throw Hdf5Failure{"Unable to allocate a unique temporary output path."};
|
|
}
|
|
|
|
Status AtomicFinalizeValidatedCandidate(
|
|
const std::filesystem::path& candidate_path,
|
|
const std::filesystem::path& output_path, const Status& validation) {
|
|
if (!validation.IsOk()) {
|
|
std::error_code ignored;
|
|
(void)std::filesystem::remove(candidate_path, ignored);
|
|
return validation;
|
|
}
|
|
if (!AtomicReplace(candidate_path, output_path)) {
|
|
std::error_code ignored;
|
|
(void)std::filesystem::remove(candidate_path, ignored);
|
|
return OutputFailure("hdf5-finalization-failure",
|
|
"The checked temporary HDF5 file could not replace "
|
|
"the final output.");
|
|
}
|
|
return Status::Ok();
|
|
}
|
|
|
|
} // namespace fesa::hdf5_internal
|