|
| 1 | +#!/usr/bin/env bash |
| 2 | +# requires: gcc |
| 3 | +# A BUILD WHOSE mcpp IS KILLED DOES NOT RACE THE NEXT ONE (2026.11.7.1, design |
| 4 | +# 2026-11-07 §4.1.2). Killing the mcpp that holds the build directory leaves |
| 5 | +# its compilers running. The next build waited only for the directory lock, so |
| 6 | +# it entered while those compilers still wrote BMIs and objects: 2026.11.5.1 |
| 7 | +# failed 3 of 75 such rounds under the default schedule and 12 of 15 under the |
| 8 | +# split schedule. Every compiler is now a participant of its build's |
| 9 | +# generation, and the next build enters only after they end. Both schedules, |
| 10 | +# twelve rounds each: the waiting build succeeds and the build after it does |
| 11 | +# nothing. |
| 12 | +set -e |
| 13 | +MCPP="${MCPP:-mcpp}" |
| 14 | +TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT |
| 15 | +make_project() { # make_project <dir> <schedule> |
| 16 | + mkdir -p "$1/src" |
| 17 | + printf '[package]\nname = "kg"\nversion = "0.1.0"\nstandard = "c++23"\n[toolchain]\ndefault = "gcc@16.1.0"\n' > "$1/mcpp.toml" |
| 18 | + [ "$2" = split ] && printf '[build]\nbmi_schedule = "on"\n' >> "$1/mcpp.toml" |
| 19 | + for i in $(seq 1 24); do |
| 20 | + printf 'export module m%d;\nimport std;\nexport int f%d(){ std::vector<int> v(%d); return (int)v.size(); }\n' "$i" "$i" "$i" > "$1/src/m$i.cppm" |
| 21 | + done |
| 22 | + { echo 'import std;'; for i in $(seq 1 24); do echo "import m$i;"; done |
| 23 | + echo 'int main(){ std::println("{}", f1()+f24()); }'; } > "$1/src/main.cpp" |
| 24 | +} |
| 25 | +for schedule in default split; do |
| 26 | + dir="$TMP/$schedule"; make_project "$dir" "$schedule" |
| 27 | + ( cd "$dir" && "$MCPP" build > "$TMP/warm.log" 2>&1 ) || { cat "$TMP/warm.log"; exit 1; } |
| 28 | + for round in $(seq 1 12); do |
| 29 | + rm -rf "$dir/target" |
| 30 | + ( cd "$dir" && exec "$MCPP" build > "$TMP/a.log" 2>&1 ) & a=$! |
| 31 | + ( sleep 0.2; cd "$dir"; rc=0; "$MCPP" build > "$TMP/b.log" 2>&1 || rc=$?; echo "$rc" > "$TMP/b.rc" ) & b=$! |
| 32 | + sleep 0.9; kill -9 "$a" 2>/dev/null || true; wait "$a" 2>/dev/null || true |
| 33 | + wait "$b" || true |
| 34 | + if [ "$(cat "$TMP/b.rc")" != 0 ]; then |
| 35 | + echo "FAIL: $schedule round $round: the build after the killed one failed"; cat "$TMP/b.log"; exit 1 |
| 36 | + fi |
| 37 | + ( cd "$dir" && "$MCPP" build > "$TMP/c.log" 2>&1 ) || { cat "$TMP/c.log"; exit 1; } |
| 38 | + if grep -q "Compiling" "$TMP/c.log"; then |
| 39 | + echo "FAIL: $schedule round $round: the following build compiled again"; cat "$TMP/c.log"; exit 1 |
| 40 | + fi |
| 41 | + out=$(cd "$dir" && "$MCPP" run 2>/dev/null | tail -1) |
| 42 | + [ "$out" = 25 ] || { echo "FAIL: $schedule round $round: the program printed '$out'"; exit 1; } |
| 43 | + done |
| 44 | + echo "OK: $schedule: 12 killed builds, each followed by a successful build and a no-op" |
| 45 | +done |
0 commit comments