mirror of
https://github.com/ProgramSnail/build_system_2022.git
synced 2025-12-06 00:48:42 +00:00
init
This commit is contained in:
parent
4d899f64a7
commit
b4a7121411
23 changed files with 681 additions and 11 deletions
14
.gitignore
vendored
14
.gitignore
vendored
|
|
@ -1,11 +1,3 @@
|
||||||
CMakeLists.txt.user
|
lib
|
||||||
CMakeCache.txt
|
bim
|
||||||
CMakeFiles
|
build
|
||||||
CMakeScripts
|
|
||||||
Testing
|
|
||||||
Makefile
|
|
||||||
cmake_install.cmake
|
|
||||||
install_manifest.txt
|
|
||||||
compile_commands.json
|
|
||||||
CTestTestfile.cmake
|
|
||||||
_deps
|
|
||||||
|
|
|
||||||
31
CMakeLists.txt
Normal file
31
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
|
project(BuildSystem)
|
||||||
|
|
||||||
|
|
||||||
|
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
|
||||||
|
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
|
||||||
|
|
||||||
|
|
||||||
|
set(BUILD_SYSTEM_SOURCES
|
||||||
|
build_graph.cpp
|
||||||
|
builder.cpp
|
||||||
|
thread_pool.cpp
|
||||||
|
task_queue.cpp
|
||||||
|
dependency_manager.cpp
|
||||||
|
task.hpp)
|
||||||
|
|
||||||
|
set (CLI_SOURCES
|
||||||
|
main.cpp
|
||||||
|
interactive_mode.cpp
|
||||||
|
file_mode.cpp
|
||||||
|
cli_common.cpp
|
||||||
|
test_tasks.hpp)
|
||||||
|
|
||||||
|
add_library(BuildSystemLib ${BUILD_SYSTEM_SOURCES})
|
||||||
|
|
||||||
|
add_executable(BuildSystemCli ${CLI_SOURCES})
|
||||||
|
target_link_libraries(BuildSystemCli BuildSystemLib)
|
||||||
|
|
||||||
|
set_property(TARGET BuildSystemLib PROPERTY CXX_STANDARD 20)
|
||||||
|
set_property(TARGET BuildSystemCli PROPERTY CXX_STANDARD 20)
|
||||||
9
README.md
Normal file
9
README.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
### Build System
|
||||||
|
|
||||||
|
Сборка:
|
||||||
|
|
||||||
|
```
|
||||||
|
mkdir build && cd build
|
||||||
|
|
||||||
|
cmake .. && make
|
||||||
|
```
|
||||||
110
build_graph.cpp
Normal file
110
build_graph.cpp
Normal file
|
|
@ -0,0 +1,110 @@
|
||||||
|
#include "build_graph.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <new>
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
const size_t BuildGraph::TraversalManager::kCurrentWayEpoch = 0;
|
||||||
|
const size_t BuildGraph::TraversalManager::kNeverVisited = 1;
|
||||||
|
|
||||||
|
class BuildGraph::TraversalManager::View {
|
||||||
|
friend TraversalManager;
|
||||||
|
|
||||||
|
public:
|
||||||
|
View() = delete;
|
||||||
|
|
||||||
|
std::vector<size_t> DFS(size_t vertex) {
|
||||||
|
std::vector<size_t> visited;
|
||||||
|
traversal_manager.DFS(vertex, visited);
|
||||||
|
return visited;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function saves exsisting values in "visited" array
|
||||||
|
void DFS(size_t vertex, std::vector<size_t>& visited) {
|
||||||
|
traversal_manager.DFS(vertex, visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~View() { traversal_manager.nextEpoch(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
View(TraversalManager &traversal_manager)
|
||||||
|
: traversal_manager(traversal_manager) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TraversalManager &traversal_manager;
|
||||||
|
};
|
||||||
|
|
||||||
|
BuildGraph::TraversalManager::View BuildGraph::TraversalManager::getView() {
|
||||||
|
return View(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this function saves existing values in "visited" array
|
||||||
|
void BuildGraph::TraversalManager::DFS(size_t vertex,
|
||||||
|
std::vector<size_t>& current_visited) {
|
||||||
|
if (visited_[vertex] == epoch_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (visited_[vertex] == kCurrentWayEpoch) {
|
||||||
|
throw IncorrectBuildGraph{};
|
||||||
|
}
|
||||||
|
|
||||||
|
visited_[vertex] = kCurrentWayEpoch;
|
||||||
|
|
||||||
|
for (auto &dependency : build_graph_.graph_[vertex].dependences) {
|
||||||
|
DFS(dependency, current_visited);
|
||||||
|
}
|
||||||
|
|
||||||
|
visited_[vertex] = epoch_;
|
||||||
|
|
||||||
|
current_visited.push_back(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildGraph::BuildGraph(const std::vector<Rule>& rules,
|
||||||
|
std::vector<Task>&& tasks)
|
||||||
|
: traversal_manager_(*this) {
|
||||||
|
graph_.reserve(tasks.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < tasks.size(); ++i) {
|
||||||
|
Target target;
|
||||||
|
target.task = std::forward<Task>(tasks[i]);
|
||||||
|
target.id = i;
|
||||||
|
graph_.push_back(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &rule : rules) {
|
||||||
|
graph_[rule.target].dependences.push_back(rule.dependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
traversal_manager_.initArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildGraph::isCorrectOrThrow(size_t target) const {
|
||||||
|
traversal_manager_.getView().DFS(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BuildGraph::isCorrect(size_t target) const {
|
||||||
|
try {
|
||||||
|
isCorrectOrThrow(target);
|
||||||
|
} catch (IncorrectBuildGraph) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Target> BuildGraph::getRequiredTargetsOrder(size_t target) const {
|
||||||
|
|
||||||
|
std::vector<Target> order;
|
||||||
|
|
||||||
|
auto rev_topsorted_required_verticles = traversal_manager_.getView().DFS(target);
|
||||||
|
|
||||||
|
order.reserve(rev_topsorted_required_verticles.size());
|
||||||
|
for (auto &vertex_id : rev_topsorted_required_verticles) {
|
||||||
|
order.push_back(graph_[vertex_id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace build_system
|
||||||
68
build_graph.hpp
Normal file
68
build_graph.hpp
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "task.hpp"
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
struct IncorrectBuildGraph {};
|
||||||
|
|
||||||
|
struct Rule {
|
||||||
|
size_t target;
|
||||||
|
size_t dependency;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Target {
|
||||||
|
size_t id;
|
||||||
|
Task task;
|
||||||
|
std::vector<size_t> dependences;
|
||||||
|
};
|
||||||
|
|
||||||
|
class BuildGraph {
|
||||||
|
private:
|
||||||
|
// manage DFS, topsort, etc. safe
|
||||||
|
class TraversalManager {
|
||||||
|
private:
|
||||||
|
class View;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TraversalManager(const BuildGraph& build_graph)
|
||||||
|
: build_graph_(build_graph), epoch_(kNeverVisited + 1) {}
|
||||||
|
|
||||||
|
void initArray() {
|
||||||
|
visited_ = std::vector<size_t>(build_graph_.graph_.size(), kNeverVisited);
|
||||||
|
}
|
||||||
|
|
||||||
|
View getView();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void nextEpoch() { ++epoch_; }
|
||||||
|
|
||||||
|
void DFS(size_t vertex, std::vector<size_t>& current_visited);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const size_t kCurrentWayEpoch;
|
||||||
|
static const size_t kNeverVisited;
|
||||||
|
|
||||||
|
const BuildGraph &build_graph_;
|
||||||
|
std::vector<size_t> visited_;
|
||||||
|
size_t epoch_;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
BuildGraph(const std::vector<Rule>& rules, std::vector<Task>&& tasks);
|
||||||
|
|
||||||
|
void isCorrectOrThrow(size_t target) const;
|
||||||
|
|
||||||
|
bool isCorrect(size_t target) const;
|
||||||
|
|
||||||
|
std::vector<Target> getRequiredTargetsOrder(size_t target) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Target> graph_;
|
||||||
|
mutable TraversalManager traversal_manager_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace build_system
|
||||||
18
builder.cpp
Normal file
18
builder.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include "builder.hpp"
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
void Builder::execute(const BuildGraph& build_graph, size_t target_id) {
|
||||||
|
// can throw IncorrectBuildGraph
|
||||||
|
auto targets_order = build_graph.getRequiredTargetsOrder(target_id);
|
||||||
|
|
||||||
|
for (auto& target : targets_order) {
|
||||||
|
thread_pool_.addTarget(std::move(target.task), target.id, target.dependences);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_pool_.start();
|
||||||
|
|
||||||
|
thread_pool_.wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace build_system
|
||||||
20
builder.hpp
Normal file
20
builder.hpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
#include "build_graph.hpp"
|
||||||
|
#include "thread_pool.hpp"
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
class Builder {
|
||||||
|
public:
|
||||||
|
explicit Builder(size_t num_threads) : thread_pool_(num_threads) {}
|
||||||
|
|
||||||
|
void execute(const BuildGraph& build_graph, size_t target_id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
ThreadPool thread_pool_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace build_system
|
||||||
94
cli_common.cpp
Normal file
94
cli_common.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
#include "cli_common.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
#include "test_tasks.hpp"
|
||||||
|
|
||||||
|
void print(const std::string& message, std::ostream* out) {
|
||||||
|
if (out) {
|
||||||
|
*out << message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void getParam(const std::string& message, T ¶m, std::istream& in, std::ostream* out) {
|
||||||
|
print(message, out);
|
||||||
|
in >> param;
|
||||||
|
}
|
||||||
|
|
||||||
|
build_system::Builder constructBuilder(std::istream& in, std::ostream* out) {
|
||||||
|
print("Builder constructor\n", out);
|
||||||
|
|
||||||
|
size_t num_threads;
|
||||||
|
getParam("num threads: ", num_threads, in, out);
|
||||||
|
|
||||||
|
return build_system::Builder(num_threads);
|
||||||
|
}
|
||||||
|
|
||||||
|
build_system::BuildGraph constructBuildGraph(std::istream& in, std::ostream* out) {
|
||||||
|
print("BuildGraph constructor\n", out);
|
||||||
|
|
||||||
|
size_t rules_count = 0;
|
||||||
|
getParam("rules count: ", rules_count, in, out);
|
||||||
|
|
||||||
|
std::vector<build_system::Rule> rules;
|
||||||
|
rules.reserve(rules_count);
|
||||||
|
|
||||||
|
print("rules (one per line, in format \"target dependency\"):\n", out);
|
||||||
|
for (size_t i = 0; i < rules_count; ++i) {
|
||||||
|
build_system::Rule rule;
|
||||||
|
in >> rule.target >> rule.dependency;
|
||||||
|
|
||||||
|
--rule.target;
|
||||||
|
--rule.dependency;
|
||||||
|
|
||||||
|
rules.push_back(rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t targets_count = 0;
|
||||||
|
getParam("targets count: ", targets_count, in, out);
|
||||||
|
|
||||||
|
std::vector<build_system::Task> tasks;
|
||||||
|
tasks.reserve(targets_count);
|
||||||
|
|
||||||
|
print("durations of tasks for targets (in seconds): ", out);
|
||||||
|
for (size_t i = 0; i < targets_count; ++i) {
|
||||||
|
size_t duration;
|
||||||
|
in >> duration;
|
||||||
|
|
||||||
|
tasks.push_back(constructTestTask(duration));
|
||||||
|
}
|
||||||
|
|
||||||
|
return build_system::BuildGraph(rules, std::move(tasks));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t constructTargetId(std::istream& in, std::ostream* out) {
|
||||||
|
size_t target_id;
|
||||||
|
getParam("target id: ", target_id, in, out);
|
||||||
|
|
||||||
|
--target_id;
|
||||||
|
|
||||||
|
return target_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute(build_system::Builder& builder,
|
||||||
|
build_system::BuildGraph& build_graph, size_t target_id,
|
||||||
|
std::ostream* out) {
|
||||||
|
try {
|
||||||
|
auto build_start_time = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
builder.execute(build_graph, target_id);
|
||||||
|
|
||||||
|
auto build_end_time = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
auto build_time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
build_end_time - build_start_time);
|
||||||
|
|
||||||
|
if (out) {
|
||||||
|
*out << "overall time: " << build_time_ms.count() << "ms\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (build_system::IncorrectBuildGraph) {
|
||||||
|
print("incorrect build graph\n", out);
|
||||||
|
}
|
||||||
|
}
|
||||||
26
cli_common.hpp
Normal file
26
cli_common.hpp
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "build_graph.hpp"
|
||||||
|
#include "builder.hpp"
|
||||||
|
|
||||||
|
void print(const std::string& message, std::ostream* out);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void getParam(const std::string& message, T& param, std::istream &in,
|
||||||
|
std::ostream* out);
|
||||||
|
|
||||||
|
build_system::Builder constructBuilder(std::istream& in,
|
||||||
|
std::ostream* out = nullptr);
|
||||||
|
|
||||||
|
build_system::BuildGraph constructBuildGraph(std::istream& in,
|
||||||
|
std::ostream* out = nullptr);
|
||||||
|
|
||||||
|
size_t constructTargetId(std::istream& in, std::ostream* out = nullptr);
|
||||||
|
|
||||||
|
void execute(build_system::Builder& builder,
|
||||||
|
build_system::BuildGraph& build_graph, size_t target_id,
|
||||||
|
std::ostream* out = nullptr);
|
||||||
1
compile_flags.txt
Normal file
1
compile_flags.txt
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
-std=c++20
|
||||||
44
dependency_manager.cpp
Normal file
44
dependency_manager.cpp
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#include "dependency_manager.hpp"
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
void DependencyManager::waitAll() {
|
||||||
|
for (size_t i = 0; i < dependency_counters_.size(); ++i) {
|
||||||
|
wait(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DependencyManager::wait(size_t id) {
|
||||||
|
auto &counter = dependency_counters_[id];
|
||||||
|
size_t counter_value = counter.load();
|
||||||
|
while (counter_value != 0) {
|
||||||
|
counter.wait(counter_value);
|
||||||
|
counter_value = counter.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t DependencyManager::add(size_t id,
|
||||||
|
const std::vector<size_t>& dependences) {
|
||||||
|
size_t new_id = dependency_counters_.size();
|
||||||
|
|
||||||
|
dependency_counters_.emplace_back(dependences.size());
|
||||||
|
|
||||||
|
tasks_consequences_.emplace_back();
|
||||||
|
|
||||||
|
for (auto& dependency_id : dependences) {
|
||||||
|
tasks_consequences_[to_new_id[dependency_id]].push_back(new_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
to_new_id[id] = new_id;
|
||||||
|
return new_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DependencyManager::done(size_t id) {
|
||||||
|
for (auto &consequence_id : tasks_consequences_[id]) {
|
||||||
|
if (dependency_counters_[consequence_id].fetch_sub(1) == 1) {
|
||||||
|
dependency_counters_[consequence_id].notify_all();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace build_system
|
||||||
29
dependency_manager.hpp
Normal file
29
dependency_manager.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <deque>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
class DependencyManager {
|
||||||
|
public:
|
||||||
|
DependencyManager() {}
|
||||||
|
|
||||||
|
void waitAll(); // async
|
||||||
|
|
||||||
|
void wait(size_t id); // async
|
||||||
|
|
||||||
|
size_t add(size_t id, const std::vector<size_t>& dependences); // sync
|
||||||
|
|
||||||
|
void done(size_t id); // async
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::deque<std::atomic<size_t>> dependency_counters_;
|
||||||
|
std::vector<std::vector<size_t>> tasks_consequences_;
|
||||||
|
std::unordered_map<size_t, size_t> to_new_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace build_system
|
||||||
25
file_mode.cpp
Normal file
25
file_mode.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "file_mode.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "build_graph.hpp"
|
||||||
|
#include "builder.hpp"
|
||||||
|
|
||||||
|
#include "cli_common.hpp"
|
||||||
|
|
||||||
|
void fileMode(const std::string& filename) {
|
||||||
|
std::ifstream in;
|
||||||
|
in.open(filename);
|
||||||
|
|
||||||
|
build_system::Builder builder = constructBuilder(in);
|
||||||
|
|
||||||
|
build_system::BuildGraph build_graph = constructBuildGraph(in);
|
||||||
|
|
||||||
|
size_t target_id = constructTargetId(in);
|
||||||
|
|
||||||
|
execute(builder, build_graph, target_id, &std::cout);
|
||||||
|
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
5
file_mode.hpp
Normal file
5
file_mode.hpp
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
void fileMode(const std::string& filename);
|
||||||
20
interactive_mode.cpp
Normal file
20
interactive_mode.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
#include "interactive_mode.hpp"
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "build_graph.hpp"
|
||||||
|
#include "builder.hpp"
|
||||||
|
|
||||||
|
#include "cli_common.hpp"
|
||||||
|
|
||||||
|
void interactiveMode() {
|
||||||
|
build_system::Builder builder = constructBuilder(std::cin, &std::cout);
|
||||||
|
|
||||||
|
build_system::BuildGraph build_graph = constructBuildGraph(std::cin, &std::cout);
|
||||||
|
|
||||||
|
size_t target_id = constructTargetId(std::cin, &std::cout);
|
||||||
|
|
||||||
|
execute(builder, build_graph, target_id, &std::cout);
|
||||||
|
|
||||||
|
}
|
||||||
3
interactive_mode.hpp
Normal file
3
interactive_mode.hpp
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
void interactiveMode();
|
||||||
10
main.cpp
Normal file
10
main.cpp
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#include "interactive_mode.hpp"
|
||||||
|
#include "file_mode.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
if (argc < 2) {
|
||||||
|
interactiveMode();
|
||||||
|
} else {
|
||||||
|
fileMode(argv[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
task.hpp
Normal file
12
task.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
using Task = std::function<void()>;
|
||||||
|
|
||||||
|
using IdentTask = std::pair<Task, size_t>;
|
||||||
|
|
||||||
|
}; // namespace build_system
|
||||||
30
task_queue.cpp
Normal file
30
task_queue.cpp
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#include "task_queue.hpp"
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
bool TaskQueue::addIdentTask(IdentTask&& task) {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
if (is_closed_) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
tasks_.push_back(std::move(task));
|
||||||
|
task_exist_.notify_one();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<IdentTask> TaskQueue::getIdentTask() {
|
||||||
|
std::unique_lock<std::mutex> lock(mutex_);
|
||||||
|
while (tasks_.empty() && !is_closed_) {
|
||||||
|
task_exist_.wait(lock);
|
||||||
|
}
|
||||||
|
if (tasks_.empty()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdentTask task = tasks_.front();
|
||||||
|
tasks_.pop_front();
|
||||||
|
|
||||||
|
return std::move(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace build_system
|
||||||
33
task_queue.hpp
Normal file
33
task_queue.hpp
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <deque>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "task.hpp"
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
class TaskQueue {
|
||||||
|
public:
|
||||||
|
TaskQueue() : is_closed_(false) {}
|
||||||
|
|
||||||
|
bool addIdentTask(IdentTask&& task);
|
||||||
|
|
||||||
|
std::optional<IdentTask> getIdentTask();
|
||||||
|
|
||||||
|
void close() {
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
is_closed_ = true;
|
||||||
|
task_exist_.notify_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::deque<IdentTask> tasks_;
|
||||||
|
bool is_closed_;
|
||||||
|
std::mutex mutex_;
|
||||||
|
std::condition_variable task_exist_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace build_system
|
||||||
13
test_tasks.hpp
Normal file
13
test_tasks.hpp
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "task.hpp"
|
||||||
|
|
||||||
|
inline build_system::Task constructTestTask(size_t duration) {
|
||||||
|
return [duration]() {
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(duration));
|
||||||
|
};
|
||||||
|
}
|
||||||
47
thread_pool.cpp
Normal file
47
thread_pool.cpp
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
#include "thread_pool.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
#include "task.hpp"
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
void ThreadPool::addTarget(Task&& task, size_t id,
|
||||||
|
const std::vector<size_t>& dependences) {
|
||||||
|
size_t new_id = dependency_manager_.add(id, dependences);
|
||||||
|
task_queue_.addIdentTask({std::move(task), new_id});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadPool::start() {
|
||||||
|
threads_.reserve(num_threads_);
|
||||||
|
for (size_t i = 0; i < num_threads_; ++i) {
|
||||||
|
threads_.emplace_back([this]() {
|
||||||
|
while (true) {
|
||||||
|
auto may_be_task = task_queue_.getIdentTask();
|
||||||
|
if (may_be_task == std::nullopt) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dependency_manager_.wait(may_be_task.value().second);
|
||||||
|
|
||||||
|
try {
|
||||||
|
may_be_task.value().first();
|
||||||
|
} catch (...) {
|
||||||
|
}
|
||||||
|
|
||||||
|
dependency_manager_.done(may_be_task.value().second);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadPool::wait() {
|
||||||
|
task_queue_.close();
|
||||||
|
dependency_manager_.waitAll();
|
||||||
|
|
||||||
|
for (auto& thread : threads_) {
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace build_system
|
||||||
30
thread_pool.hpp
Normal file
30
thread_pool.hpp
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "dependency_manager.hpp"
|
||||||
|
#include "task_queue.hpp"
|
||||||
|
|
||||||
|
namespace build_system {
|
||||||
|
|
||||||
|
class ThreadPool {
|
||||||
|
public:
|
||||||
|
explicit ThreadPool(size_t num_threads) : num_threads_(num_threads) {}
|
||||||
|
|
||||||
|
void addTarget(Task&& task, size_t id, const std::vector<size_t>& dependences);
|
||||||
|
|
||||||
|
void start();
|
||||||
|
|
||||||
|
void wait();
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t num_threads_;
|
||||||
|
std::vector<std::thread> threads_;
|
||||||
|
TaskQueue task_queue_;
|
||||||
|
DependencyManager dependency_manager_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}; // namespace build_system
|
||||||
Loading…
Add table
Add a link
Reference in a new issue