From 7cb92a40910d6a775debf54854660a0c3ac2bae5 Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sat, 20 Jun 2026 08:55:48 +0200 Subject: [PATCH] Build docs with a pre-configured docker --- .gitea/workflows/docs.yaml | 3 --- Dockerfile.builder | 8 ++++++++ include/kpn/scheduler.hpp | 15 ++++++++++++++- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/.gitea/workflows/docs.yaml b/.gitea/workflows/docs.yaml index f4650cb..596d487 100644 --- a/.gitea/workflows/docs.yaml +++ b/.gitea/workflows/docs.yaml @@ -22,9 +22,6 @@ jobs: with: fetch-depth: 0 # full history needed for mkdocs gh-deploy - - name: Install MkDocs dependencies - run: pip install --quiet -r docs/requirements.txt - - name: Configure git identity run: | git config user.name "Gitea Actions" diff --git a/Dockerfile.builder b/Dockerfile.builder index e5300d5..c2cb9e1 100644 --- a/Dockerfile.builder +++ b/Dockerfile.builder @@ -16,4 +16,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ nodejs \ && rm -rf /var/lib/apt/lists/* +# Pre-install MkDocs dependencies so the docs workflow does not need to pip +# install at runtime. --break-system-packages is required because the Debian +# base marks the environment as externally managed (PEP 668); this is safe in +# a dedicated container image. +COPY docs/requirements.txt /tmp/docs-requirements.txt +RUN pip install --no-cache-dir --break-system-packages -r /tmp/docs-requirements.txt \ + && rm /tmp/docs-requirements.txt + WORKDIR /src diff --git a/include/kpn/scheduler.hpp b/include/kpn/scheduler.hpp index d449b74..e7920bf 100644 --- a/include/kpn/scheduler.hpp +++ b/include/kpn/scheduler.hpp @@ -69,6 +69,9 @@ public: while (!q->pq.empty()) q->pq.pop(); total_.fetch_sub(discarded, std::memory_order_relaxed); } + // Lock cv_mx_ before notifying so the stop signal can't be lost in the + // gap between a worker's predicate check and its wait() (see submit()). + { std::lock_guard lk(cv_mx_); } cv_.notify_all(); for (auto& t : workers_) if (t.joinable()) t.join(); workers_.clear(); @@ -91,6 +94,12 @@ public: } total_.fetch_add(1, std::memory_order_relaxed); submitted_.fetch_add(1, std::memory_order_relaxed); + // Synchronize with worker_loop's predicate evaluation: taking cv_mx_ + // here guarantees a worker is either before its predicate check (and + // will observe total_ > 0) or already blocked in wait() (and will be + // woken). Without this, notify_one() can slip into the gap between the + // worker's predicate check and its wait(), and be lost — a deadlock. + { std::lock_guard lk(cv_mx_); } cv_.notify_one(); } @@ -157,8 +166,12 @@ private: active_.fetch_sub(1, std::memory_order_relaxed); // Notify drain() if this was the last in-flight task. // acq_rel ensures the decrement is visible before any drain() load. - if (total_.fetch_sub(1, std::memory_order_acq_rel) == 1) + // Lock drain_mx_ before notifying to avoid a lost wakeup against + // drain()'s predicate check (same hazard as submit()/cv_mx_). + if (total_.fetch_sub(1, std::memory_order_acq_rel) == 1) { + { std::lock_guard lk(drain_mx_); } drain_cv_.notify_all(); + } } void worker_loop(std::size_t id) {