summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Cristian Sarbu <paul.cristian.sarbu@huawei.com>2022-08-22 13:35:01 +0200
committerPaul Cristian Sarbu <paul.cristian.sarbu@huawei.com>2022-12-21 14:42:51 +0100
commitb09a5eaed4dcadc12b3a3c7d0ac14ce03c1c2ca0 (patch)
tree8723b375d9cdc68df0ec271443d54be98654b1b2 /src
parent84c4ae51a3f4aa60f08c576d955e200909798d7c (diff)
downloadjustbuild-b09a5eaed4dcadc12b3a3c7d0ac14ce03c1c2ca0.tar.gz
Just-MR: Add critical git op map
Diffstat (limited to 'src')
-rw-r--r--src/other_tools/TARGETS1
-rw-r--r--src/other_tools/ops_maps/TARGETS13
-rw-r--r--src/other_tools/ops_maps/critical_git_op_map.cpp49
-rw-r--r--src/other_tools/ops_maps/critical_git_op_map.hpp115
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