From 356314406f0eabfe551f4e067bb7eb7762f6ad15 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Mon, 9 Sep 2024 18:57:08 -0400 Subject: [PATCH] Now using a std::async and future to do an async popen but I also need the FILE read in read_line to be async, so now I'm at a point where I have to refactor into a better statemachine. --- builder.cpp | 37 ++++++++++++++++++++++----- threadtest.cpp | 69 +++++++++++++++++++------------------------------- 2 files changed, 57 insertions(+), 49 deletions(-) diff --git a/builder.cpp b/builder.cpp index 2b1edc2..e59cbe2 100644 --- a/builder.cpp +++ b/builder.cpp @@ -19,10 +19,12 @@ #include #include #include +#include using std::string; using namespace fmt; using namespace nlohmann; +using namespace std::chrono_literals; #define BUF_MAX 1024 @@ -37,6 +39,7 @@ Builder::Builder(GUI &g, GameEngine &engine) } FILE *start_command(string &build_cmd) { + println(">>>>>>>>> start_command {}", build_cmd); FILE *build_out = popen(build_cmd.c_str(), "r"); dbc::check(build_out != nullptr, "Failed to run command."); return build_out; @@ -80,9 +83,10 @@ MatchResult Builder::parse_line(const string &line) { } enum BuildState { - WAITING, BUILDING, DONE + WAITING, BUILDING, DONE, STARTING, READING }; + void Builder::run() { git_repository* repo = nullptr; @@ -104,7 +108,9 @@ void Builder::run() { FILE *build_out = NULL; bool build_done = false; + string line = ""; BuildState state = WAITING; + std::future build_fut; fileWatcher->watch(); @@ -115,15 +121,34 @@ void Builder::run() { game.start_round(); gui.building(); gui.output(format("CHANGES! Running build {}", build_cmd)); - build_out = start_command(build_cmd); - state = BUILDING; + build_fut = std::async([&]() { + return start_command(build_cmd); + }); + state = STARTING; } break; + case STARTING: { + println(">>> STARTING"); + std::future_status status = build_fut.wait_for(0ms); + + if(status == std::future_status::ready) { + build_out = build_fut.get(); + state = READING; + } else { + state = STARTING; + } + } + break; + + case READING: { + line = read_line(build_out, build_done); + state = BUILDING; + } + break; case BUILDING: { + println(">>> BUILDING"); // check if there's output - string line = read_line(build_out, build_done); - if(build_done) { bool good = end_build(build_out, gui, build_cmd); @@ -143,7 +168,7 @@ void Builder::run() { gui.output(format("HIT WITH {} @ {}:{}:{} {}", m.type, m.file_name, m.lnumber, m.col, m.message)); game.hit(m.type); } - state = BUILDING; + state = READING; } } break; diff --git a/threadtest.cpp b/threadtest.cpp index f90f2a2..9d59991 100644 --- a/threadtest.cpp +++ b/threadtest.cpp @@ -1,54 +1,37 @@ -#include +#include +#include #include -#include -#include #include - -std::mutex m; -std::condition_variable cv; -std::string data; -bool ready = false; -bool processed = false; - -void worker_thread() -{ - // wait until main() sends data - std::unique_lock lk(m); - cv.wait(lk, []{ return ready; }); - - // after the wait, we own the lock - std::cout << "Worker thread is processing data\n"; - data += " after processing"; - - // send data back to main() - processed = true; - std::cout << "Worker thread signals data processing completed\n"; - - // manual unlocking is done before notifying, to avoid waking up - // the waiting thread only to block again (see notify_one for details) - lk.unlock(); - cv.notify_one(); -} +using namespace std::chrono_literals; int main() { - std::thread worker(worker_thread); - - data = "Example data"; - // send data to the worker thread + std::future future = std::async(std::launch::async, []() { - std::lock_guard lk(m); - ready = true; - std::cout << "main() signals data ready for processing\n"; - } - cv.notify_one(); + std::this_thread::sleep_for(3s); + return 8; + }); + + std::cout << "waiting...\n"; + std::future_status status; - // wait for the worker + do { - std::unique_lock lk(m); - cv.wait(lk, []{ return processed; }); + status = future.wait_for(100ms); + switch (status) + { + case std::future_status::deferred: + std::cout << "deferred\n"; + break; + case std::future_status::timeout: + std::cout << "timeout\n"; + break; + case std::future_status::ready: + std::cout << "ready!\n"; + break; + } } - std::cout << "Back in main(), data = " << data << '\n'; + while (status != std::future_status::ready); - worker.join(); + std::cout << "result is " << future.get() << '\n'; }