diff --git a/builder.cpp b/builder.cpp index 7f36717..378595a 100644 --- a/builder.cpp +++ b/builder.cpp @@ -36,71 +36,60 @@ Builder::Builder(GUI &g, GameEngine &engine) config["build_cmd"].template get_to(build_cmd); } -void Builder::run_build() { - std::regex err_re("(.*?):([0-9]+):([0-9]+):\\s*(.*?):\\s*(.*)\n*"); - - char buffer[BUF_MAX]; // BUF_MAX is a define already? - std::ofstream stats_out; - - - stats_out.open("stats.csv", std::ios::out | std::ios::app); - std::time_t tstamp = std::time(nullptr); - - dbc::check(stats_out.good(), "Error opening stats.csv file."); - dbc::pre("simple test", [&]() { return stats_out.good(); }); - - - // need to catch the error message when the command is bad +FILE *start_command(string &build_cmd) { FILE *build_out = popen(build_cmd.c_str(), "r"); dbc::check(build_out != nullptr, "Failed to run command."); + return build_out; +} - game.start_round(); - - while(fgets(buffer, BUF_MAX, build_out) != nullptr) { - string line(buffer); // yeah, that's probably a problem - - std::smatch err; - bool match = std::regex_match(line, err, err_re); - - if(match) { - string file_name = err[1].str(); - string lnumber = err[2].str(); - string col = err[3].str(); - string type = err[4].str(); - string message = err[5].str(); - - string result = format("{:%FT%T},{},{},{},{},{}", - fmt::localtime(tstamp), file_name, - lnumber, col, type, message); - - stats_out << result; - gui.output(format("HIT WITH {} @ {}:{}:{} {}", type, file_name, lnumber, col, message)); - - game.hit(type); +string read_line(FILE *build_out, bool &done_out) { + char buffer[BUF_MAX]; + char *res = fgets(buffer, BUF_MAX, build_out); + //!!! exception if res == nullptr + done_out = res == nullptr; - // refactor this - if(game.is_dead()) { - gui.you_died(); - } - } + if(!done_out) { + return string{buffer}; // yeah, that's probably a problem + } else { + return ""; } +} - - game.end_round(); - +void end_build(FILE *build_out, GUI &gui, string &build_cmd) { int rc = pclose(build_out); + if(rc == 0) { gui.build_works(); } else { - gui.output(format(">>>>>>>> DIED? {}", game.is_dead())); - gui.build_failed(!game.is_dead(), build_cmd); + // BUG: make it not play two sounds + gui.build_failed(true, build_cmd); } +} + +void Builder::run_build(const string &line) { + std::regex err_re("(.*?):([0-9]+):([0-9]+):\\s*(.*?):\\s*(.*)\n*"); + + std::smatch err; + bool match = std::regex_match(line, err, err_re); - stats_out.close(); - dbc::post("a post test", [&]() { return !stats_out.is_open(); }); + if(match) { + string file_name = err[1].str(); + string lnumber = err[2].str(); + string col = err[3].str(); + string type = err[4].str(); + string message = err[5].str(); + + gui.output(format("HIT WITH {} @ {}:{}:{} {}", type, file_name, lnumber, col, message)); + + game.hit(type); + } } +enum BuildState { + WAITING, BUILDING, DONE +}; + void Builder::run() { git_repository* repo = nullptr; @@ -120,22 +109,53 @@ void Builder::run() { gui.output(format("Watching directory {} for changes...", git_path)); efsw::WatchID wid = fileWatcher->addWatch(git_path, listener, true); - int rc = gui.main_loop(game, [&] { - fileWatcher->watch(); + FILE *build_out = NULL; + bool build_done = false; + BuildState state = WAITING; - if(listener->changes) { - gui.building(); - gui.output(format("CHANGES! Running build {}", build_cmd)); + fileWatcher->watch(); - run_build(); + int rc = gui.main_loop(game, [&] { + switch(state) { + case WAITING: + if(listener->changes) { + game.start_round(); + gui.building(); + gui.output(format("CHANGES! Running build {}", build_cmd)); + build_out = start_command(build_cmd); + state = BUILDING; + } + break; + + case BUILDING: { + // check if there's output + string line = read_line(build_out, build_done); + if(build_done) { + end_build(build_out, gui, build_cmd); + build_out = NULL; + state = DONE; + } else { + // get the command line + // run the build if it's available + run_build(line); + state = BUILDING; + } + } + break; + + case DONE: { + game.end_round(); if(game.is_dead()) { - gui.output("!!!! YOU DIED! !!!! Learn to code luser."); + gui.you_died(); game.reset(); } listener->reset_state(); gui.output("^^^^^^^^^^^ END ^^^^^^^^^^^"); + state = WAITING; + } + break; } return 0; diff --git a/builder.hpp b/builder.hpp index ace5c38..51ef193 100644 --- a/builder.hpp +++ b/builder.hpp @@ -1,6 +1,7 @@ #pragma once #include "gui.hpp" #include "game_engine.hpp" +#include using std::string; @@ -14,7 +15,7 @@ class Builder { Builder(GUI &g, GameEngine &engine); - void run_build(); + void run_build(const string &line); void run(); }; diff --git a/gui.cpp b/gui.cpp index 1f35d51..131b66e 100644 --- a/gui.cpp +++ b/gui.cpp @@ -85,6 +85,7 @@ void GUI::build_failed(bool play_sound, const string &command) { void GUI::you_died() { building_sound.stop(); you_died_sound.play(); + output("!!!! YOU DIED! !!!! Learn to code luser."); output("YOU DIED!"); } diff --git a/meson.build b/meson.build index ae63084..481e0cf 100644 --- a/meson.build +++ b/meson.build @@ -46,6 +46,9 @@ executable('jsontest', 'jsontest.cpp', executable('threadtest', 'threadtest.cpp', dependencies: dependencies) +executable('badref', 'badref.cpp', + dependencies: dependencies) + executable('corotest', [ 'corotest.cpp' ], diff --git a/threadtest.cpp b/threadtest.cpp index 22b19c6..f90f2a2 100644 --- a/threadtest.cpp +++ b/threadtest.cpp @@ -1,34 +1,54 @@ -#include -#include -#include -#include -#include -#include +#include #include #include +#include +#include -using namespace std::chrono_literals; +std::mutex m; +std::condition_variable cv; +std::string data; +bool ready = false; +bool processed = false; -std::atomic_int counter = 0; -std::mutex counter_mutex; +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"; -void locked_counter() { - for (int i = 0; i < 5; ++i) - { - std::lock_guard lock(counter_mutex); - std::this_thread::sleep_for(100ms); - std::cout << "Thread 1 executing\n"; - ++counter; - } + // 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(); } int main() { - std::jthread t2(locked_counter); // pass by value + std::thread worker(worker_thread); + + data = "Example data"; + // send data to the worker thread + { + std::lock_guard lk(m); + ready = true; + std::cout << "main() signals data ready for processing\n"; + } + cv.notify_one(); + + // wait for the worker + { + std::unique_lock lk(m); + cv.wait(lk, []{ return processed; }); + } + std::cout << "Back in main(), data = " << data << '\n'; - for(int i = 0; i < 5; ++i) { - std::lock_guard lock(counter_mutex); - std::cout << "counter is " << counter << std::endl; - } + worker.join(); }