sdl_runtime_compiler git · main
SDL3 game for running and compiling code at runtime
C++ 72.3% C 26.2%git clone https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler.gitwget https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler/archive/sdl_runtime_compiler.tar.gzAdd compile threaded With Claude
src/systems/compiler_system.cpp | 106 +++++++++++++++++++++++++--------------- src/systems/compiler_system.hpp | 17 +++++-- 2 files changed, 79 insertions(+), 44 deletions(-) diff --git a/src/systems/compiler_system.cpp b/src/systems/compiler_system.cpp index 5b2f4c3..554008c 100644 --- a/src/systems/compiler_system.cpp +++ b/src/systems/compiler_system.cpp @@ -1,17 +1,28 @@ #include <SDL3/SDL.h> +#include <algorithm> #include "systems/compiler_system.hpp" #include "text_3d/text_3d.hpp" #include "utils/ascii.hpp" +CompilerSystem::~CompilerSystem() +{ + if (_worker.joinable()) _worker.join(); +} + void CompilerSystem::compile(TextWallList& texts, const std::string& src) { + // One compile at a time. Reap a finished worker; ignore re-triggers while + // one is still running. + if (_running.load()) return; + if (_worker.joinable()) _worker.join(); + last_compile_ok = false; - // Clear previous output from PosX wall + // Clear previous output from the compiler wall (main thread — GL-owned). texts.erase( std::remove_if(texts.begin(), texts.end(), [](const TextWallPtr& wt) { @@ -20,51 +31,73 @@ CompilerSystem::compile(TextWallList& texts, const std::string& src) texts.end()); _count = 0; - // Write source to temp file + { + std::lock_guard<std::mutex> lock(_mtx); + _pending.clear(); + } + + _running.store(true); + _worker = std::thread(&CompilerSystem::run_Compile, this, src); +} + + +// Worker thread: write source, run gcc, collect output lines. Touches NO game +// state except _pending (under _mtx) and _running. Never builds a mesh. +void +CompilerSystem::run_Compile(CompilerSystem* self, std::string src) +{ FILE* f = fopen("/tmp/game_prog.c", "w"); if (!f) { - SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "CompilerSystem: could not write temp file"); + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, + "CompilerSystem: could not write temp file"); + self->_running.store(false); return; } fputs(src.c_str(), f); fclose(f); - // Open gcc _pipe — stderr redirected to stdout so we get errors too - if (_pipe) { pclose(_pipe); _pipe = nullptr; } - _pipe = popen("LC_ALL=C gcc /tmp/game_prog.c -o /tmp/game_prog 2>&1", "r"); - if (!_pipe) SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "CompilerSystem: popen failed"); + FILE* pipe = popen("LC_ALL=C gcc /tmp/game_prog.c -o /tmp/game_prog 2>&1", "r"); + if (!pipe) { + SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "CompilerSystem: popen failed"); + self->_running.store(false); + return; + } + + int n = 0; + char buf[512]; + while (fgets(buf, sizeof(buf), pipe)) { // blocking read is fine here + std::string line(buf); + if (!line.empty() && line.back() == '\n') line.pop_back(); + if (line.empty()) continue; + + std::lock_guard<std::mutex> lock(self->_mtx); + self->_pending.push_back(line); + ++n; + } + pclose(pipe); // blocking wait is fine here + + if (n == 0) { // no diagnostics → success + std::lock_guard<std::mutex> lock(self->_mtx); + self->_pending.push_back("OK"); + } + + self->_running.store(false); } void CompilerSystem::update(TextWallList& texts) { - if (!_pipe) return; - - char buf[512]; - // Non-blocking: try to read a line, but don't stall the frame - // popen is blocking by default so we set fd non-blocking - int fd = fileno(_pipe); - fd_set set; FD_ZERO(&set); FD_SET(fd, &set); - struct timeval tv{0, 0}; // zero timeout = poll - - while (select(fd+1, &set, nullptr, nullptr, &tv) > 0) { - if (!fgets(buf, sizeof(buf), _pipe)) { - // EOF — compile finished - pclose(_pipe); - _pipe = nullptr; - if (_count == 0) - push_Line(texts, "OK"); - return; - } - std::string line(buf); - // Strip trailing newline - if (!line.empty() && line.back() == '\n') line.pop_back(); - if (!line.empty()) - push_Line(texts, line); - - FD_ZERO(&set); FD_SET(fd, &set); + // Drain whatever the worker produced and render it (main thread, GL-safe). + DynArray<std::string> lines; + { + std::lock_guard<std::mutex> lock(_mtx); + if (_pending.empty()) return; + std::swap(lines, _pending); } + + for (const std::string& line : lines) + push_Line(texts, line); } @@ -87,12 +120,7 @@ CompilerSystem::push_Line(TextWallList& texts, const std::string& line) Text3dPtr t = make_Text3d(clean, Text3d::depth_compiler, 0.003f, pos, ConfigColor::white); t->yaw = tf.yaw; - TextWallPtr wt = cpputils::make_unique<TextWall>(TextWall{ - tf.wall_position, - Wall::Role::Compiler, - std::move(t) - }); - - texts.push_back(std::move(wt)); + texts.push_back(cpputils::make_unique<TextWall>(TextWall{ + tf.wall_position, Wall::Role::Compiler, std::move(t) })); _count++; } diff --git a/src/systems/compiler_system.hpp b/src/systems/compiler_system.hpp index 20f1a40..c96db89 100644 --- a/src/systems/compiler_system.hpp +++ b/src/systems/compiler_system.hpp @@ -1,6 +1,9 @@ #pragma once #include <string> #include <cstdio> +#include <thread> +#include <mutex> +#include <atomic> #include "entities/world.hpp" #include "utils/globals.hpp" @@ -8,19 +11,23 @@ struct CompilerSystem { - private: - FILE* _pipe {nullptr}; - int _count {0}; + std::thread _worker; + std::mutex _mtx; + DynArray<std::string> _pending; + std::atomic<bool> _running{false}; + int _count {0}; - void push_Line(TextWallList&, const std::string& line); + void push_Line (TextWallList&, const std::string& line); + static void run_Compile(CompilerSystem* self, std::string src); public: static inline float line_height = 0.4f; static inline float start_y = World::get_FloorY()+1; bool last_compile_ok{false}; + ~CompilerSystem(); void update (TextWallList&); void compile (TextWallList&, const std::string& src); - bool is_Running() const { return _pipe != nullptr; } + bool is_Running() const { return _running.load(); } };