Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / commits / d91b3f4

sdl_runtime_compiler git · main

SDL3 game for running and compiling code at runtime

sdl3 c++ compiler dlopen cmake · first commit 2026-04-19 · last commit 2026-07-03 (3 months ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/sdl-runtime-compiler

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.gz

Add compile threaded With Claude

Christian Immanuel · 2026-06-06 01:11 · d91b3f4b05d8199e448902f26ac97bba0da2c3cd

 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(); }
 };