summaryrefslogtreecommitdiff
path: root/src/buildtool/execution_api/local/file_storage.hpp
diff options
context:
space:
mode:
authorOliver Reiche <oliver.reiche@huawei.com>2022-03-09 18:11:20 +0100
committerOliver Reiche <oliver.reiche@huawei.com>2022-03-09 18:11:20 +0100
commitb44ba08941f87c3f03fefd54cab2d12012fa01ca (patch)
treef6521829a63356283df9a5cf1d61214e28a14ca6 /src/buildtool/execution_api/local/file_storage.hpp
parenteddf9107720bbcce6e2cebf4b7cfa63e8e7ede2c (diff)
downloadjustbuild-b44ba08941f87c3f03fefd54cab2d12012fa01ca.tar.gz
FileStorage: Avoid rename for owned files
... and therefore split the common `AtomicAdd()` into two functions for adding from bytes or file path. The procedure for adding from bytes remains the same. For adding from file path, we can link the file directly and skip the rename, if the file did not exist and we have ownership.
Diffstat (limited to 'src/buildtool/execution_api/local/file_storage.hpp')
-rw-r--r--src/buildtool/execution_api/local/file_storage.hpp62
1 files changed, 50 insertions, 12 deletions
diff --git a/src/buildtool/execution_api/local/file_storage.hpp b/src/buildtool/execution_api/local/file_storage.hpp
index 01e8085d..ec7714f6 100644
--- a/src/buildtool/execution_api/local/file_storage.hpp
+++ b/src/buildtool/execution_api/local/file_storage.hpp
@@ -30,7 +30,7 @@ class FileStorage {
std::filesystem::path const& source_path,
bool is_owner = false) const noexcept
-> bool {
- return AtomicAdd(id, source_path, is_owner);
+ return AtomicAddFromFile(id, source_path, is_owner);
}
/// \brief Add bytes to storage.
@@ -38,7 +38,7 @@ class FileStorage {
[[nodiscard]] auto AddFromBytes(std::string const& id,
std::string const& bytes) const noexcept
-> bool {
- return AtomicAdd(id, bytes, /*is_owner=*/true);
+ return AtomicAddFromBytes(id, bytes);
}
[[nodiscard]] auto GetPath(std::string const& name) const noexcept
@@ -50,23 +50,62 @@ class FileStorage {
std::filesystem::path const storage_root_{};
static constexpr bool fd_less_{kType == ObjectType::Executable};
- /// \brief Add file to storage via copy and atomic rename.
+ /// \brief Add file to storage from file path via link or copy and rename.
+ /// If a race-condition occurs, the winning thread will be the one
+ /// performing the link/rename operation first or last, depending on kMode
+ /// being set to FirstWins or LastWins, respectively. All threads will
+ /// signal success.
+ /// \returns true if file exists afterward.
+ [[nodiscard]] auto AtomicAddFromFile(std::string const& id,
+ std::filesystem::path const& path,
+ bool is_owner) const noexcept -> bool {
+ auto file_path = storage_root_ / id;
+ if ((kMode == StoreMode::LastWins or
+ not FileSystemManager::Exists(file_path)) and
+ FileSystemManager::CreateDirectory(file_path.parent_path())) {
+ auto direct_create = kMode == StoreMode::FirstWins and is_owner;
+ auto create_directly = [&]() {
+ // Entry does not exist and we are owner of the file (e.g., file
+ // generated in the execution directory). Try to hard link it
+ // directly or check its existence if it was created by now.
+ return FileSystemManager::CreateFileHardlinkAs<kType>(
+ path, file_path) or
+ FileSystemManager::IsFile(file_path);
+ };
+ auto create_and_stage = [&]() {
+ // Entry exists and we need to overwrite it, or we are not owner
+ // of the file. Create the file in a process/thread-local
+ // temporary path and stage it.
+ auto unique_path = CreateUniquePath(file_path);
+ return unique_path and
+ CreateFileFromPath(*unique_path, path, is_owner) and
+ StageFile(*unique_path, file_path);
+ };
+ if (direct_create ? create_directly() : create_and_stage()) {
+ Logger::Log(
+ LogLevel::Trace, "created entry {}.", file_path.string());
+ return true;
+ }
+ }
+ return FileSystemManager::IsFile(file_path);
+ }
+
+ /// \brief Add file to storage from bytes via write and atomic rename.
/// If a race-condition occurs, the winning thread will be the one
/// performing the rename operation first or last, depending on kMode being
/// set to FirstWins or LastWins, respectively. All threads will signal
/// success.
/// \returns true if file exists afterward.
- template <class T>
- [[nodiscard]] auto AtomicAdd(std::string const& id,
- T const& data,
- bool is_owner) const noexcept -> bool {
+ [[nodiscard]] auto AtomicAddFromBytes(
+ std::string const& id,
+ std::string const& bytes) const noexcept -> bool {
auto file_path = storage_root_ / id;
if (kMode == StoreMode::LastWins or
not FileSystemManager::Exists(file_path)) {
auto unique_path = CreateUniquePath(file_path);
if (unique_path and
FileSystemManager::CreateDirectory(file_path.parent_path()) and
- CreateFileFromData(*unique_path, data, is_owner) and
+ CreateFileFromBytes(*unique_path, bytes) and
StageFile(*unique_path, file_path)) {
Logger::Log(
LogLevel::Trace, "created entry {}.", file_path.string());
@@ -77,7 +116,7 @@ class FileStorage {
}
/// \brief Create file from file path.
- [[nodiscard]] static auto CreateFileFromData(
+ [[nodiscard]] static auto CreateFileFromPath(
std::filesystem::path const& file_path,
std::filesystem::path const& other_path,
bool is_owner) noexcept -> bool {
@@ -92,10 +131,9 @@ class FileStorage {
}
/// \brief Create file from bytes.
- [[nodiscard]] static auto CreateFileFromData(
+ [[nodiscard]] static auto CreateFileFromBytes(
std::filesystem::path const& file_path,
- std::string const& bytes,
- bool /*unused*/) noexcept -> bool {
+ std::string const& bytes) noexcept -> bool {
// Write executables without opening any writeable file descriptors in
// this process to avoid those from being inherited by child processes.
return FileSystemManager::WriteFileAs<kType>(