diff --git a/CLAUDE.md b/CLAUDE.md index 753f15bddb..73d23dfbac 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Perry is a native TypeScript compiler written in Rust that compiles TypeScript source code directly to native executables. It uses SWC for TypeScript parsing and LLVM for code generation. -**Current Version:** 0.5.1460 +**Current Version:** 0.5.1461 ## TypeScript Parity Status @@ -82,7 +82,8 @@ cargo build --release # Build all crates cargo build --profile perry-dev -p perry # Fast local dev build (#5422; perry-dev profile) cargo build --release -p perry-runtime -p perry-stdlib # Rebuild runtime (MUST rebuild stdlib too!) cargo build --release -p perry-runtime-static -p perry-stdlib-static # Emit libperry_{runtime,stdlib}.a (#5422: runtime/stdlib are now rlib-only; the .a comes from these wrapper crates) -cargo test --release --workspace \ +RUST_TEST_THREADS=1 cargo test --release -p perry-runtime # MUST be single-threaded (see below) +cargo test --release --workspace --exclude perry-runtime \ --exclude perry-ui-ios --exclude perry-ui-tvos --exclude perry-ui-watchos \ --exclude perry-ui-visionos --exclude perry-ui-android --exclude perry-ui-windows \ --exclude perry-ui-gtk4 # Run tests (exclude cross-host UI crates on macOS) @@ -236,6 +237,7 @@ Build outputs are invisible to `git status`, so a clean tree tells you nothing a - **addr-class ratchet** (`scripts/addr_class_inventory.py`) — a file gaining a bare-address site fails `lint`. - **`conformance-smoke` shards are flaky.** Before believing a red shard, re-run it and A/B the named tests against a pristine `main` build; several are already in `test-parity/known_failures.json`. - **Integration suites under `crates/*/tests/*.rs` do not run per-PR** (nightly/tag only) — a regression there can land green and sit red for days. Prefer putting acceptance coverage in `cargo-test`-visible unit tests (#5960). +- **`perry-runtime`'s tests are not parallel-safe — run them `RUST_TEST_THREADS=1`.** They share process-global side tables (#1444), and ~180 readers are not required to take the clearing lock (see `gc::tests::global_sink_isolation`'s header; #7672 is converting them to `per_test_global!` one at a time). Every CI path already pins `RUST_TEST_THREADS=1`; a local `cargo test --workspace` does NOT, which is the whole gap. Measured on the default pool: 600 full-suite runs at `--test-threads=16` (load ~90) were clean, but at `--test-threads=64` (load ~115) **2 of 320 runs failed** — in `proxy::…numeric_write_guard…` and `array::element_shape::matrix_tests::matrix_delete_revokes`, neither related to the change under test. The tell is the message: these fail on their own *fixture precondition* ("fixture must start proven, or every verdict below is vacuous"), i.e. another thread wiped the global, not a real defect in the code under test. Chasing such a failure as if it were a regression is wasted days. ### ★ Four ways a gate can be unable to fail diff --git a/Cargo.lock b/Cargo.lock index 54362bfd8c..be4d2cedf5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5547,7 +5547,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "perry" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "base64", @@ -5607,14 +5607,14 @@ dependencies = [ [[package]] name = "perry-api-manifest" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "serde", ] [[package]] name = "perry-audio-miniaudio" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "cc", "libc", @@ -5622,7 +5622,7 @@ dependencies = [ [[package]] name = "perry-codegen" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "inkwell", @@ -5639,7 +5639,7 @@ dependencies = [ [[package]] name = "perry-codegen-arkts" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "perry-hir", @@ -5647,7 +5647,7 @@ dependencies = [ [[package]] name = "perry-codegen-glance" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "perry-hir", @@ -5655,7 +5655,7 @@ dependencies = [ [[package]] name = "perry-codegen-js" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "perry-dispatch", @@ -5664,7 +5664,7 @@ dependencies = [ [[package]] name = "perry-codegen-swiftui" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "perry-hir", @@ -5672,7 +5672,7 @@ dependencies = [ [[package]] name = "perry-codegen-wasm" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "base64", @@ -5684,7 +5684,7 @@ dependencies = [ [[package]] name = "perry-codegen-wear-tiles" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "perry-hir", @@ -5692,7 +5692,7 @@ dependencies = [ [[package]] name = "perry-container-compose" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "async-trait", @@ -5721,14 +5721,14 @@ dependencies = [ [[package]] name = "perry-container-e2e" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", ] [[package]] name = "perry-diagnostics" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "serde", "serde_json", @@ -5736,7 +5736,7 @@ dependencies = [ [[package]] name = "perry-dispatch" -version = "0.5.1460" +version = "0.5.1461" [[package]] name = "perry-doc-fixture-my-bindings" @@ -5747,7 +5747,7 @@ dependencies = [ [[package]] name = "perry-doc-tests" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "clap", @@ -5762,7 +5762,7 @@ dependencies = [ [[package]] name = "perry-ext-ads" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "block2", "objc2", @@ -5772,7 +5772,7 @@ dependencies = [ [[package]] name = "perry-ext-argon2" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "argon2", "perry-ffi", @@ -5780,7 +5780,7 @@ dependencies = [ [[package]] name = "perry-ext-axios" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "reqwest", @@ -5789,7 +5789,7 @@ dependencies = [ [[package]] name = "perry-ext-bcrypt" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "bcrypt", "perry-ffi", @@ -5797,7 +5797,7 @@ dependencies = [ [[package]] name = "perry-ext-better-sqlite3" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "rusqlite", @@ -5805,7 +5805,7 @@ dependencies = [ [[package]] name = "perry-ext-cheerio" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "scraper", @@ -5813,7 +5813,7 @@ dependencies = [ [[package]] name = "perry-ext-commander" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "perry-runtime", @@ -5821,7 +5821,7 @@ dependencies = [ [[package]] name = "perry-ext-cron" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "chrono", "cron", @@ -5831,7 +5831,7 @@ dependencies = [ [[package]] name = "perry-ext-dayjs" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "chrono", "perry-ffi", @@ -5839,7 +5839,7 @@ dependencies = [ [[package]] name = "perry-ext-decimal" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "rust_decimal", @@ -5847,7 +5847,7 @@ dependencies = [ [[package]] name = "perry-ext-dotenv" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "serde_json", @@ -5855,7 +5855,7 @@ dependencies = [ [[package]] name = "perry-ext-ethers" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "rand 0.10.1", @@ -5863,7 +5863,7 @@ dependencies = [ [[package]] name = "perry-ext-events" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "perry-runtime", @@ -5871,14 +5871,14 @@ dependencies = [ [[package]] name = "perry-ext-exponential-backoff" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-fastify" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "bytes", "http-body-util", @@ -5896,7 +5896,7 @@ dependencies = [ [[package]] name = "perry-ext-fetch" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "bytes", "lazy_static", @@ -5909,7 +5909,7 @@ dependencies = [ [[package]] name = "perry-ext-http" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "bytes", "h2", @@ -5933,7 +5933,7 @@ dependencies = [ [[package]] name = "perry-ext-ioredis" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "lazy_static", "perry-ffi", @@ -5943,7 +5943,7 @@ dependencies = [ [[package]] name = "perry-ext-jsonwebtoken" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "base64", "jsonwebtoken", @@ -5954,7 +5954,7 @@ dependencies = [ [[package]] name = "perry-ext-lru-cache" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "lru", "perry-ffi", @@ -5963,7 +5963,7 @@ dependencies = [ [[package]] name = "perry-ext-moment" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "chrono", "perry-ffi", @@ -5971,7 +5971,7 @@ dependencies = [ [[package]] name = "perry-ext-mongodb" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "bson", "futures-util", @@ -5983,7 +5983,7 @@ dependencies = [ [[package]] name = "perry-ext-mysql2" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "chrono", "perry-ffi", @@ -5993,7 +5993,7 @@ dependencies = [ [[package]] name = "perry-ext-nanoid" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "nanoid", "perry-ffi", @@ -6002,7 +6002,7 @@ dependencies = [ [[package]] name = "perry-ext-net" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "bytes", "perry-ffi", @@ -6015,7 +6015,7 @@ dependencies = [ [[package]] name = "perry-ext-node-forge" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "const-oid 0.9.6", "der 0.7.10", @@ -6034,7 +6034,7 @@ dependencies = [ [[package]] name = "perry-ext-nodemailer" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "lettre", "perry-ffi", @@ -6044,7 +6044,7 @@ dependencies = [ [[package]] name = "perry-ext-pdf" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "printpdf", @@ -6052,7 +6052,7 @@ dependencies = [ [[package]] name = "perry-ext-pg" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "sqlx", @@ -6061,7 +6061,7 @@ dependencies = [ [[package]] name = "perry-ext-ratelimit" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "governor", "perry-ffi", @@ -6069,7 +6069,7 @@ dependencies = [ [[package]] name = "perry-ext-sharp" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "fast_image_resize", "image", @@ -6079,14 +6079,14 @@ dependencies = [ [[package]] name = "perry-ext-slugify" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", ] [[package]] name = "perry-ext-streams" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "lazy_static", "perry-ffi", @@ -6095,7 +6095,7 @@ dependencies = [ [[package]] name = "perry-ext-undici" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "perry-runtime", @@ -6104,7 +6104,7 @@ dependencies = [ [[package]] name = "perry-ext-uuid" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "uuid", @@ -6112,7 +6112,7 @@ dependencies = [ [[package]] name = "perry-ext-validator" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ffi", "regex", @@ -6122,7 +6122,7 @@ dependencies = [ [[package]] name = "perry-ext-ws" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "futures-util", "lazy_static", @@ -6135,7 +6135,7 @@ dependencies = [ [[package]] name = "perry-ext-zlib" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "brotli", "flate2", @@ -6145,7 +6145,7 @@ dependencies = [ [[package]] name = "perry-ffi" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "dashmap", "once_cell", @@ -6154,7 +6154,7 @@ dependencies = [ [[package]] name = "perry-hir" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "perry-api-manifest", @@ -6172,7 +6172,7 @@ dependencies = [ [[package]] name = "perry-parser" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "perry-diagnostics", @@ -6184,7 +6184,7 @@ dependencies = [ [[package]] name = "perry-runtime" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "base64", @@ -6226,14 +6226,14 @@ dependencies = [ [[package]] name = "perry-runtime-static" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-runtime", ] [[package]] name = "perry-stdlib" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "aes 0.8.4", "aes 0.9.1", @@ -6328,14 +6328,14 @@ dependencies = [ [[package]] name = "perry-stdlib-static" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-stdlib", ] [[package]] name = "perry-transform" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "perry-hir", @@ -6344,14 +6344,14 @@ dependencies = [ [[package]] name = "perry-ui" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ui-model", ] [[package]] name = "perry-ui-android" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "base64", "itoa", @@ -6368,7 +6368,7 @@ dependencies = [ [[package]] name = "perry-ui-geisterhand" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "rand 0.10.1", "serde", @@ -6378,7 +6378,7 @@ dependencies = [ [[package]] name = "perry-ui-gtk4" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "base64", "cairo-rs 0.22.0", @@ -6401,7 +6401,7 @@ dependencies = [ [[package]] name = "perry-ui-ios" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "base64", "block2", @@ -6417,7 +6417,7 @@ dependencies = [ [[package]] name = "perry-ui-macos" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "base64", "block2", @@ -6432,7 +6432,7 @@ dependencies = [ [[package]] name = "perry-ui-model" -version = "0.5.1460" +version = "0.5.1461" [[package]] name = "perry-ui-test" @@ -6443,11 +6443,11 @@ dependencies = [ [[package]] name = "perry-ui-testkit" -version = "0.5.1460" +version = "0.5.1461" [[package]] name = "perry-ui-tvos" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "base64", "block2", @@ -6463,7 +6463,7 @@ dependencies = [ [[package]] name = "perry-ui-visionos" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "base64", "block2", @@ -6479,7 +6479,7 @@ dependencies = [ [[package]] name = "perry-ui-watchos" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "block2", "libc", @@ -6492,7 +6492,7 @@ dependencies = [ [[package]] name = "perry-ui-windows" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "base64", "libc", @@ -6509,14 +6509,14 @@ dependencies = [ [[package]] name = "perry-ui-windows-winui" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "perry-ui-windows", ] [[package]] name = "perry-updater" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "anyhow", "base64", @@ -6532,7 +6532,7 @@ dependencies = [ [[package]] name = "perry-wasm-host" -version = "0.5.1460" +version = "0.5.1461" dependencies = [ "wasmi", ] diff --git a/Cargo.toml b/Cargo.toml index 431c2eb967..be1119fc8d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -315,7 +315,7 @@ codegen-units = 16 codegen-units = 16 [workspace.package] -version = "0.5.1460" +version = "0.5.1461" edition = "2021" license = "MIT" repository = "https://github.com/PerryTS/perry" diff --git a/changelog.d/7791-perry-runtime-tests-single-threaded.md b/changelog.d/7791-perry-runtime-tests-single-threaded.md new file mode 100644 index 0000000000..635163cf88 --- /dev/null +++ b/changelog.d/7791-perry-runtime-tests-single-threaded.md @@ -0,0 +1,16 @@ +Document that `perry-runtime`'s tests must run single-threaded locally, matching what every CI path already does. + +**Why.** `gc::tests::root_words::bare_address_in_shadow_slot_survives_a_real_collection` was reported failing on a clean `c2a96b638` with the box at load ~111, passing in isolation and passing three full-suite reruns on a lightly loaded box. It was investigated as a possible load-dependent GC bug. It is not one. + +**What it actually is.** `perry-runtime`'s tests share process-global side tables. `test.yml` has pinned `RUST_TEST_THREADS=1` for this crate since #1444 — the comment there says the default pool "races the GC/threading tests into intermittent SIGSEGV" — and `gc::tests::global_sink_isolation`'s header records that ~180 readers are still not required to take the clearing lock (#7672 converts them to `per_test_global!` one table at a time). The command CLAUDE.md documented for local runs, `cargo test --release --workspace`, does not pin the thread count, so it runs this crate in exactly the configuration CI exists to avoid. That divergence is the bug being fixed here. + +**Evidence.** Against `2e5bf4434` (release, includes #7317 so the seeded-schedule machinery is present): + +- 4 × 150 full-suite runs at `--test-threads=16`, sustained load ~90: **600/600 clean**, 0 vacuous. +- 4 × 80 full-suite runs at `--test-threads=64`, load ~115: **2 failures in 320** — `proxy::tests::object_array_numeric_write_guard_requires_complete_uniform_proof` and `array::element_shape::matrix_tests::matrix_delete_revokes`. Neither is `root_words`; the failing test is simply whichever one loses the race. +- Both failures are the crate's own vacuity guards firing ("fixture must start proven, or every verdict below is vacuous"; "one-field loops should publish one non-zero 16-bit lane") — a test finding its *precondition* destroyed by a concurrent thread, not a GC invariant violated. That distinction is what rules out the GC-bug reading. +- `root_words` itself did not fail once in 920 parallel full-suite runs, 1500 targeted `root_words`+`global_sink_isolation` pairings, or a 40-seed `PERRY_GC_SCHEDULE_SEED` sweep (rate 25%) — it never appears in that sweep's failures. + +So the reported symptom is real and load-dependent, but it belongs to the test harness, not the collector. No test is `#[ignore]`d and no retry is added: the fix is to stop documenting the unsupported configuration. + +**Note for the seeded-schedule sweep.** `PERRY_GC_SCHEDULE_SEED` is a whole-process env var, and `scripts/gc_schedule_fuzz.sh` takes a *compiled binary*, not the unit-test suite. Setting it across `cargo test` fails 8 GC tests at every seed (several assert the unset behaviour outright, e.g. `schedule::unset_is_inert_for_evacuation_policy`). That is the tool being used outside its contract, not a defect — recorded here so the next person does not re-derive it.