Skip to content

No Windows parity suite for [BUILTIN-PROCESS-FAILURE]: the Win32 spawn unwind is source-only and no CI job executes it #228

Description

@MelbourneDeveloper

[BUILTIN-PROCESS-FAILURE] (docs/specs/0012-Built-InFunctions.md) is a
platform-neutral contract: every one of the five spawn_process_with_handler
failure codes must leave the process exactly as the call found it — no
descriptor or handle held, no child unreaped, no table slot occupied, one handle
number spent. Both implementations claim it. Only one is tested.

Current state

POSIX is properly pinned. compiler/runtime/test_system_failures.c injects
each failure deterministically through the seams in
compiler/runtime/test_spawn_seam.h (static-link interposition of fork,
pthread_create, pthread_mutex_init and the allocator, with the real symbol
recovered via dlsym(RTLD_NEXT, ...)), and for every code asserts: the exact
return value, an unchanged live-allocation census, an unchanged lowest free
descriptor, the actual table slot released (measured with next_handle_number(),
not inferred from the error code), waitpid(-1, &st, WNOHANG) == -1 && errno == ECHILD, the exact next handle number, and a successful recovery spawn.

Win32 has none of that. The Windows branch of
system_runtime.c:392-677 had
several real acquisition-unwind defects; they are fixed in source — the two
CreatePipe calls are split so a failed second one does not strand the first,
-4/-5/-6 all unwind through an abandon_process that destroys the record
mutex and frees the command and record, SetHandleInformation failures are
checked and unwound, and the -5 arm now waits unconditionally after requesting
termination. Every one of those is source-only. Nothing executes them.

Why CI does not catch it

.github/workflows/ci-windows.yml runs
under msys2/MinGW and builds the runtime archives (make _runtime CC=gcc AR=ar)
plus exactly one C test (make _test_gc_stack_root). It never runs
make _test_c_runtime, so no row of the Makefile's C_TEST_SUITES table
executes on Windows — test_system_runtime included. The Windows unwind could
be deleted wholesale and every required check would stay green.

The POSIX seams cannot be reused: dlsym/RTLD_NEXT, fork and the pthread
interposers have no Win32 equivalent, so test_spawn_seam.h will not compile
there even under MinGW.

What is needed

  1. Win32 seams. Thin, injectable wrappers around CreatePipe,
    SetHandleInformation, CreateProcessA and the record allocation, each
    armable to fail with a chosen GetLastError(). Because MinGW compiles the
    _WIN32 branch normally, this can live beside the POSIX suite rather than in
    a separate harness — the seam header is the only part that needs an #ifdef.
  2. A parity suite asserting, per failure code, the Win32 analogue of every
    POSIX assertion above: exact return value, handle-count inventory across the
    call (GetProcessHandleCount), the actual table slot released, confirmed
    child termination (WaitForSingleObject returning WAIT_OBJECT_0 and an exit
    code), the exact next handle number, and a successful recovery spawn.
  3. A CI step that runs it. Add make _test_c_runtime (or a Windows-capable
    subset) to ci-windows.yml as a job-level step. Per CLAUDE.md it must not
    be gated with on: paths: — a path-filtered required check never reports and
    blocks every merge forever.

Why this is filed rather than fixed

Writing Win32 test code that cannot be compiled or executed on the authoring
machine, and landing it straight into a required gate, is how a check that
asserts nothing gets added. The seams and the suite should be developed by
someone who can iterate against a real windows-latest runner, or against a
MinGW container, with the mutation check applied the same way the POSIX suite
had it: revert each unwind fix in turn and confirm the suite goes red.

Until then the branch ships POSIX-only evidence for a contract both
implementations claim, and the Windows half is unproven.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions