Skip to content

Windows validation work order for the turnloop migration (PR #10354) #10385

Description

@proggeramlug

Nothing in the turnloop migration has run on Windows. Not one line of P4 through P11 — the blocking pool, the HTTP/HTTPS servers, TLS, fetch, the four database drivers, per-agent loops, or the CLI. Every lane report says so in its own "not done" section.

Windows was a binding scope decision for this project from day one, and it is the platform where the design is least exercised: turnloop's IOCP backend is structurally different from epoll and kqueue, not a port of them. This ticket is the work order for a Windows machine.

Branch under test: turnloop/integration (PR #10354). Everything below is against that branch versus main at its merge base.


0. Setup

# Toolchain — pinned. Do NOT `rustup update`.
rustup toolchain install nightly-2026-08-20
# LLVM 22.1.8; set LLVM_SYS_221_PREFIX to its root.
# Node: the .node-version file at the repo root is authoritative (26.5.1). Install exactly that.
git clone https://github.com/PerryTS/perry.git
cd perry
git checkout turnloop/integration
npm ci --ignore-scripts --no-audit --no-fund     # REQUIRED: without it, package-backed gap fixtures silently stop testing anything

Build the coherent package set in one cargo invocation — the wrappers must come from the same build as the stdlib archive (#7629), or ext-routed tests fail with "the wrapper archive bundles a DIFFERENT tokio compilation":

cargo build --release -p perry -p perry-runtime-static -p perry-stdlib-static `
  -p perry-ext-http -p perry-ext-net -p perry-ext-ws -p perry-ext-zlib

Set PERRY_RUNTIME_DIR explicitly to that target\release for every run.

Step 0 is itself a result. If the branch does not build on Windows, stop and report that — it is the single most valuable thing this machine can tell us today.


1. Highest value: the gap suite

$env:PERRY_SKIP_BUILD=1
.\scripts\run_gap_tests.sh        # via git-bash, or the documented Windows entry point

Run it on both turnloop/integration and its merge base on main, from source, and compare per test — not by totals. The bar is zero status changes. Nine parity failures are expected on both arms (three of them are already red against the committed snapshot on main).

Report the two failure sets in full, and the per-test diff.


2. The fixtures that exist because of this migration

These are the subjects. Run each and report Perry's output beside Node 26.5.1's:

test_gap_turnloop_net_sockets          test_gap_turnloop_http_server
test_gap_turnloop_https_server         test_gap_turnloop_keepalive_timeout
test_gap_turnloop_http_lifecycle       test_gap_turnloop_p2_process
test_gap_turnloop_p4_pool              test_gap_turnloop_fetch
test_gap_turnloop_fetch_pool_wait      test_gap_turnloop_p9_worker_agent_net
test_gap_turnloop_listen_conflict      test_gap_turnloop_listen_error

The 14 test_gap_http2_* fixtures are expected to fail — they are a ratchet for work in progress. Report them, but a failure there is not a Windows finding.


3. The connection ceiling, which is a genuine Windows open question

The ceiling was raised from 4,096 handles to 65,536 (net_config() in crates/perry-runtime/src/event_pump/agent_loop.rs) because turnloop 0.1.0-alpha.5 pages its slot tables, making a large ceiling free. On Linux, verified: 10,000 of 10,000 connections open, idle RSS ~50 MB, 6,228 bytes per connection.

On Windows this needs checking independently, because IOCP's OVERLAPPED slab is deliberately not paged — it maps a completion packet's pointer to an op index by pointer arithmetic over one allocation, so it is still sized eagerly by max_operations (currently 32,768). resources, workers and watch::entries are paged; that slab is not.

Measure:

  • idle RSS of a turnloop HTTP server (compare against the main arm);
  • how many concurrent connections it actually accepts — is 2,048 gone on Windows too?
  • RSS holding 10,000 idle connections, both arms.

If the idle cost on Windows is materially worse than Linux's, that is a finding and max_operations may need a platform-specific value. Say so rather than accepting it.


4. Behaviours with Windows-specific semantics

Each of these has a known Windows difference. Compare against Node on the same machine.

  • listen() twice on one port must give EADDRINUSE, not two live servers. Windows' errno differs from Linux's (-4091 vs -98); the fixture asserts the sign, not the value.
  • A bind that fails must emit 'error', not print to stderr and hang.
  • SO_REUSEPORT is refused on Windows by design — Windows has no equivalent, and SO_REUSEADDR there permits hijacking rather than sharing, so it must never be silently mapped onto it. Confirm nothing is trying.
  • child_process (P2): TerminateProcess only starts termination, and CRT descriptor passing goes through STARTUPINFOW.lpReserved2. Exercise spawn, stdio pipes, kill and exit codes.
  • Sockets and completion ports: an association is permanent and inescapable — neither WSADuplicateSocketW nor DuplicateHandle escapes it. Anything doing a descriptor handoff is worth extra attention.
  • WSAECONNABORTED: Windows reports the local end of a departed peer as ConnectionAborted where Unix gives ConnectionReset. turnloop folds the two together everywhere; a bug of exactly this shape was found and fixed in turnloop last week. If you see a spurious "peer did not finish shutdown", suspect it.

5. Lower priority, still wanted

cargo test --release --workspace --exclude perry-ui-macos --exclude perry-ui-ios `
  --exclude perry-ui-tvos --exclude perry-ui-watchos --exclude perry-ui-visionos `
  --exclude perry-ui-android --exclude perry-ui-gtk4
node scripts/node_compat_matrix.mjs --check

perry-runtime's tests are not parallel-safe — run them RUST_TEST_THREADS=1.


What to report

For each section: what you ran, what happened, and Node's output beside Perry's where they differ. Please report failures verbatim rather than summarised — a Windows-only failure is likely to be something none of us has seen, and the exact text matters.

Two things worth more than a pass:

  • Anything that hangs. Several bugs in this migration presented as a hang rather than an error, and a hang is the failure mode a summary loses.
  • Anything you cannot run. "This step did not work on Windows and here is why" is a genuine result. Please do not skip a step silently — a gap we know about is worth far more than one we assume is covered.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions