diff options
author | Paul Cristian Sarbu <paul.cristian.sarbu@huawei.com> | 2022-08-22 13:35:01 +0200 |
---|---|---|
committer | Paul Cristian Sarbu <paul.cristian.sarbu@huawei.com> | 2022-12-21 14:42:51 +0100 |
commit | b09a5eaed4dcadc12b3a3c7d0ac14ce03c1c2ca0 (patch) | |
tree | 8723b375d9cdc68df0ec271443d54be98654b1b2 | |
parent | 84c4ae51a3f4aa60f08c576d955e200909798d7c (diff) | |
download | justbuild-b09a5eaed4dcadc12b3a3c7d0ac14ce03c1c2ca0.tar.gz |
Just-MR: Add critical git op map
-rw-r--r-- | src/other_tools/TARGETS | 1 | ||||
-rw-r--r-- | src/other_tools/ops_maps/TARGETS | 13 | ||||
-rw-r--r-- | src/other_tools/ops_maps/critical_git_op_map.cpp | 49 | ||||
-rw-r--r-- | src/other_tools/ops_maps/critical_git_op_map.hpp | 115 |
4 files changed, 178 insertions, 0 deletions
diff --git a/src/other_tools/TARGETS b/src/other_tools/TARGETS new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/src/other_tools/TARGETS @@ -0,0 +1 @@ +{} diff --git a/src/other_tools/ops_maps/TARGETS b/src/other_tools/ops_maps/TARGETS new file mode 100644 index 00000000..8bde49eb --- /dev/null +++ b/src/other_tools/ops_maps/TARGETS @@ -0,0 +1,13 @@ +{ "critical_git_op_map": + { "type": ["@", "rules", "CC", "library"] + , "name": ["critical_git_op_map"] + , "hdrs": ["critical_git_op_map.hpp"] + , "srcs": ["critical_git_op_map.cpp"] + , "deps": + [ ["src/buildtool/multithreading", "async_map_consumer"] + , ["src/other_tools/git_operations", "git_operations"] + , ["src/utils/cpp", "hash_combine"] + ] + , "stage": ["src", "other_tools", "ops_maps"] + } +} diff --git a/src/other_tools/ops_maps/critical_git_op_map.cpp b/src/other_tools/ops_maps/critical_git_op_map.cpp new file mode 100644 index 00000000..a365a1d8 --- /dev/null +++ b/src/other_tools/ops_maps/critical_git_op_map.cpp @@ -0,0 +1,49 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "src/other_tools/ops_maps/critical_git_op_map.hpp" + +// define the mapping to actual operations being called +GitOpKeyMap const GitOpKey::map_ = { + {GitOpType::INITIAL_COMMIT, CriticalGitOps::GitInitialCommit}, + {GitOpType::ENSURE_INIT, CriticalGitOps::GitEnsureInit}, + {GitOpType::KEEP_TAG, CriticalGitOps::GitKeepTag}, + {GitOpType::GET_HEAD_ID, CriticalGitOps::GitGetHeadId}, + {GitOpType::GET_BRANCH_REFNAME, CriticalGitOps::GitGetBranchRefname}}; + +/// \brief Create a CriticalOpMap object +auto CreateCriticalGitOpMap(CriticalGitOpGuardPtr const& crit_git_op_ptr) + -> CriticalGitOpMap { + auto crit_op_runner = [crit_git_op_ptr](auto /*unused*/, + auto setter, + auto logger, + auto subcaller, + auto const& key) { + auto curr_key = crit_git_op_ptr->FetchAndSetCriticalKey(key); + if (curr_key == std::nullopt) { + // do critical operation now + (*setter)(key.operation(key.params, logger)); + } + else { + // do critical operation after curr_key was processed + (*subcaller)( + {*curr_key}, + [key, setter, logger](auto const& /*unused*/) { + (*setter)(key.operation(key.params, logger)); + }, + logger); + } + }; + return AsyncMapConsumer<GitOpKey, GitOpValue>(crit_op_runner); +} diff --git a/src/other_tools/ops_maps/critical_git_op_map.hpp b/src/other_tools/ops_maps/critical_git_op_map.hpp new file mode 100644 index 00000000..bb9b2765 --- /dev/null +++ b/src/other_tools/ops_maps/critical_git_op_map.hpp @@ -0,0 +1,115 @@ +// Copyright 2022 Huawei Cloud Computing Technology Co., Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef INCLUDED_SRC_OTHER_TOOLS_OPS_MAPS_CRITICAL_GIT_OP_MAP_HPP +#define INCLUDED_SRC_OTHER_TOOLS_OPS_MAPS_CRITICAL_GIT_OP_MAP_HPP + +#include <filesystem> +#include <mutex> +#include <string> + +#include "src/buildtool/multithreading/async_map_consumer.hpp" +#include "src/other_tools/git_operations/git_operations.hpp" +#include "src/utils/cpp/hash_combine.hpp" + +using GitOpKeyMap = std::unordered_map< + GitOpType, + std::function<GitOpValue(GitOpParams const&, + AsyncMapConsumerLoggerPtr const&)>>; + +struct GitOpKey { + GitOpParams params{"", "", ""}; /* key (with exceptions) */ + GitOpType op_type{GitOpType::DEFAULT_OP}; /* key */ + + [[nodiscard]] auto operation(GitOpParams const& params, + AsyncMapConsumerLoggerPtr const& logger) const + -> GitOpValue { + return map_.at(op_type)(params, logger); + } + + [[nodiscard]] auto operator==(GitOpKey const& other) const -> bool { + return params == other.params && op_type == other.op_type; + } + + private: + static GitOpKeyMap const map_; +}; + +class CriticalGitOpGuard; +using CriticalGitOpGuardPtr = std::shared_ptr<CriticalGitOpGuard>; + +using CriticalGitOpMap = AsyncMapConsumer<GitOpKey, GitOpValue>; + +[[nodiscard]] auto CreateCriticalGitOpMap( + CriticalGitOpGuardPtr const& crit_git_op_ptr) -> CriticalGitOpMap; + +/// \brief Class ensuring thread safety in critical Git operations. +/// By always storing the most recent operation to be executed, a chain of +/// operations can be created such that no thread is left in a blocking state. +/// Each repo has its own key, so the caller has to ensure the target_path +/// parameter provided is non-empty. +class CriticalGitOpGuard { + public: + [[nodiscard]] auto FetchAndSetCriticalKey(GitOpKey const& new_key) + -> std::optional<GitOpKey> { + // making sure only one thread at a time processes this section + std::scoped_lock<std::mutex> const lock(critical_key_mutex_); + auto target_path_key = + std::filesystem::hash_value(new_key.params.target_path); + if (curr_critical_key_.contains(target_path_key)) { + GitOpKey old_key = + curr_critical_key_[target_path_key]; // return stored key + curr_critical_key_[target_path_key] = + new_key; // replace stored key with new one + return old_key; + } + return std::nullopt; + // mutex released when lock goes out of scope + } + + private: + std::unordered_map<size_t, GitOpKey> curr_critical_key_{}; + std::mutex critical_key_mutex_; +}; + +namespace std { +template <> +struct hash<GitOpParams> { + [[nodiscard]] auto operator()(GitOpParams const& ct) const noexcept + -> std::size_t { + size_t seed{}; + // hash_value is used due to a bug in stdlibc++ which makes + // std::hash<std::filesystem::path>{}() fail with `temporary of type + // '__hash_enum<std::filesystem::__cxx11::path>' has private destructor` + hash_combine<size_t>(&seed, + std::filesystem::hash_value(ct.target_path)); + hash_combine<std::string>(&seed, ct.git_hash); + hash_combine<std::string>(&seed, ct.branch); + return seed; + } +}; + +template <> +struct hash<GitOpKey> { + [[nodiscard]] auto operator()(GitOpKey const& ct) const noexcept + -> std::size_t { + size_t seed{}; + hash_combine<GitOpParams>(&seed, ct.params); + hash_combine<>(&seed, ct.op_type); + return seed; + } +}; +} // namespace std + +#endif // INCLUDED_SRC_OTHER_TOOLS_OPS_MAPS_CRITICAL_GIT_OP_MAP_HPP
\ No newline at end of file |