split out coroutine stuff into make_task

master
Daniel Kolesa 2018-04-22 21:07:22 +02:00
parent 097722b1ed
commit d4037f9c1d
1 changed files with 54 additions and 34 deletions

View File

@ -212,6 +212,37 @@ private:
bool p_action = false; bool p_action = false;
}; };
struct make_task {
make_task() = delete;
make_task(
string_range target, std::vector<std::string> deps, make_rule &rl
) {
p_coro = std::make_unique<coroutine<void()>>(
[target, deps = std::move(deps), &rl](auto) {
std::vector<string_range> rdeps;
for (auto &s: deps) {
rdeps.push_back(s);
}
rl.call(target, iterator_range<string_range *>(
rdeps.data(), rdeps.data() + rdeps.size()
));
}
);
p_coro->resume();
}
bool done() const {
return !*p_coro;
}
void resume() {
p_coro->resume();
}
private:
std::unique_ptr<coroutine<void()>> p_coro{};
};
struct make { struct make {
make(int threads = std::thread::hardware_concurrency()) { make(int threads = std::thread::hardware_concurrency()) {
p_tpool.start(threads); p_tpool.start(threads);
@ -264,8 +295,8 @@ private:
template<typename F> template<typename F>
void wait_for(F func) { void wait_for(F func) {
std::queue<std::unique_ptr<coroutine<void()>>> coros; std::queue<make_task> tasks;
p_waiting.push(&coros); p_waiting.push(&tasks);
try { try {
func(); func();
} catch (...) { } catch (...) {
@ -273,35 +304,35 @@ private:
throw; throw;
} }
p_waiting.pop(); p_waiting.pop();
if (coros.empty()) { if (tasks.empty()) {
/* nothing to wait for, so return */ /* nothing to wait for, so return */
return; return;
} }
/* cycle until coroutines are done */ /* cycle until tasks are done */
std::unique_lock<std::mutex> lk{p_mtx}; std::unique_lock<std::mutex> lk{p_mtx};
while (!p_avail) { while (!p_avail) {
p_cond.wait(lk); p_cond.wait(lk);
} }
std::queue<std::unique_ptr<coroutine<void()>>> acoros; std::queue<make_task> atasks;
while (!coros.empty()) { while (!tasks.empty()) {
p_avail = false; p_avail = false;
while (!coros.empty()) { while (!tasks.empty()) {
try { try {
auto c = std::move(coros.front()); auto t = std::move(tasks.front());
coros.pop(); tasks.pop();
c->resume(); t.resume();
if (*c) { if (!t.done()) {
/* still not dead, re-push */ /* still not dead, re-push */
acoros.push(std::move(c)); atasks.push(std::move(t));
} }
} catch (make_error const &) { } catch (make_error const &) {
writeln("waiting for the remaining tasks to finish..."); writeln("waiting for the remaining tasks to finish...");
for (; !coros.empty(); coros.pop()) { for (; !tasks.empty(); tasks.pop()) {
try { try {
auto c = std::move(coros.front()); auto t = std::move(tasks.front());
coros.pop(); tasks.pop();
while (*c) { while (!t.done()) {
c->resume(); t.resume();
} }
} catch (make_error const &) { } catch (make_error const &) {
/* no rethrow */ /* no rethrow */
@ -310,10 +341,10 @@ private:
throw; throw;
} }
} }
if (acoros.empty()) { if (atasks.empty()) {
break; break;
} }
coros.swap(acoros); tasks.swap(atasks);
/* so we're not busylooping */ /* so we're not busylooping */
while (!p_avail) { while (!p_avail) {
p_cond.wait(lk); p_cond.wait(lk);
@ -361,20 +392,9 @@ private:
} }
} }
if (rl && (rl->action() || detail::check_exec(tname, rdeps))) { if (rl && (rl->action() || detail::check_exec(tname, rdeps))) {
auto coro = std::make_unique<coroutine<void()>>( make_task t{tname, std::move(rdeps), *rl};
[tname, rdeps, rl](auto) { if (!t.done()) {
std::vector<string_range> rdepsl; p_waiting.top()->push(std::move(t));
for (auto &s: rdeps) {
rdepsl.push_back(s);
}
rl->call(tname, iterator_range<string_range *>(
rdepsl.data(), rdepsl.data() + rdepsl.size()
));
}
);
coro->resume();
if (*coro) {
p_waiting.top()->push(std::move(coro));
} }
} }
} }
@ -452,7 +472,7 @@ private:
std::mutex p_mtx{}; std::mutex p_mtx{};
std::condition_variable p_cond{}; std::condition_variable p_cond{};
std::stack<std::queue<std::unique_ptr<coroutine<void()>>> *> p_waiting{}; std::stack<std::queue<make_task> *> p_waiting{};
bool p_avail = false; bool p_avail = false;
}; };