Windows: cosmocc cc1 blocks indefinitely (~0% CPU) on large C translation units, intermittently, at -O0

Author: farkasmarkCreated Sep 9, 2026Updated Sep 9, 2026

Summary

Compiling a large C translation unit with cosmocc on Windows intermittently hangs. cc1 stops making progress and sits at ~0% CPU indefinitely while holding its temporary .s output open. It is not an ICE, not an error, and not a slow compile - the process simply blocks and never returns.

Measured rate on GitHub Actions windows-latest: ~12% per compile of a large TU, at -O0, on both x86_64-linux-cosmo and aarch64-linux-cosmo. It did not reproduce on a Windows 11 desktop in 40 compiles, so the rate is environment-dependent - see below; that contrast is probably the most useful thing in this report.

This is distinct from #1488 (cosmoc++ / std::sort), which is deterministic, C++-only, and does not occur at -O0. This one is intermittent, plain C, and happens at -O0.

Cheap reproducer (no third-party tree needed)

Two downloads and a loop. The SQLite amalgamation is a convenient large TU because it is one self-contained file; QuickJS's quickjs.c hangs identically, so nothing here is SQLite-specific.

bash
# 1. toolchain
curl -fsSLO https://cosmo.zip/pub/cosmocc/cosmocc-4.0.2.zip
mkdir -p cosmo && unzip -q cosmocc-4.0.2.zip -d cosmo
export PATH="$PWD/cosmo/bin:$PATH"

# 2. a large C translation unit (8.6 MB, ~239k lines)
curl -fsSLO https://sqlite.org/2024/sqlite-amalgamation-3450100.zip
unzip -q sqlite-amalgamation-3450100.zip
cd sqlite-amalgamation-3450100

# 3. compile it in a loop. A healthy compile takes ~5s; anything past ~90s is wedged.
for i in $(seq 1 40); do
  start=$(date +%s)
  timeout -k 5 90 cosmocc -std=c11 -O0 -w -c -o /tmp/out.o sqlite3.c >/dev/null 2>&1
  rc=$?
  echo "iter $i rc=$rc elapsed=$(( $(date +%s) - start ))s"
  [ "$rc" = 124 ] && echo "  ^^ WEDGED"
done

Run under an MSYS2 shell on Windows. On the affected host this wedges roughly once per eight compiles - 24 iterations give ~95% confidence of catching at least one - and rc=124 is the timeout firing on a cc1 that never returned. Note the caveat below: this does not fire everywhere.

To see that it is blocked rather than spinning, sample the stuck process twice while it hangs - only the Win32 process table exposes a native process's cumulative CPU:

powershell
foreach ($i in 1..2) {
  Get-Process cc1 -ErrorAction SilentlyContinue |
    Select-Object Id, @{n='cpu';e={$_.CPU}}, @{n='rssMB';e={[math]::Round($_.WorkingSet64/1MB,1)}}
  Start-Sleep 5
}

Evidence

A watchdog firing after 120s of silence, sampling every build process twice 5 seconds apart:

WEDGED after 120s of silence
pid=5744 ppid=7140 cc1  cpu=3.42s  dcpu=0.01s  rss=219.4MB
    cmd: ...\libexec\gcc\aarch64-linux-cosmo\14.1.0\cc1 -quiet -nostdinc
         -I vendor/sqlite -imultiarch aarch64-linux-cosmo ...
         vendor/sqlite/sqlite3.c -quiet -dumpdir build/.aarch64/
         -dumpbase sqlite3.c ... -O0 -std=c11 -fportcosmo ...
         -o D:\a\_temp\msys64\tmp\ccfs5cmy.s
pid=7140 ppid=5396 aarch64-linux-cosmo-gcc  cpu=0.09s  dcpu=0s
pid=8512 ppid=500  make.exe                 cpu=0.66s  dcpu=0.04s

dcpu=0.01s over a 5-second window is the key measurement: cc1 is blocked, not spinning. It accumulates 3-7s of CPU, then stops, consistently at roughly the same point, holding its temp .s open.

Rate measurement

250 solo compiles of sqlite3.c across 10 parallel jobs, each looping until it wedged:

first wedge at iteration: 1, 1, 2, 2, 3, 8, 11, 13, 19, 23
mean 8.3 iterations

A geometric distribution with p = 0.12 has mean 1/p = 8.3, matching a constant ~12% per-compile probability. At whole-build level (one sqlite3.c plus one quickjs.c, -j2) that surfaces as ~30% of builds hanging: 6/20 observed.

The rate is environment-dependent (and this narrows it)

The same reproducer, same cosmocc 4.0.2, same -O0, same MSYS2 shell, run on a Windows 11 Pro 26200 desktop: 0 wedges in 40 consecutive compiles, every one finishing in 4-5s.

That is not merely "did not reproduce" - it is statistically incompatible with the CI rate. If p were 0.12, the chance of 40 clean compiles is 0.88^40 = 0.6%. Zero events in 40 puts a 95% upper bound of 3/40 = 7.5% on the rate for this host, and the observation sits far below the CI figure.

So whatever the trigger is, it is not simply "cosmocc compiling a large TU on Windows". Something about the GitHub Actions Windows image differs: candidates worth checking are its ephemeral D:\ work volume, Defender / real-time scanning on the temp path cc1 writes its .s into, or that image's particular MSYS2 installation. The temp-directory arm below relocated TMPDIR/TMP/TEMP within that image and did not clear it, which does not rule out the volume or the scanner.

If it would help, I can run any arm on either host - the desktop gives a clean control, which the CI-only data did not have.

What has been ruled out

Each tested rather than reasoned about, both arms in a single dispatch on the same image:

Hypothesis Test Result
Optimizer / codegen blow-up CPU delta while wedged No. dcpu ~ 0; a blow-up would burn CPU.
MSYS2 temp directory 10 runs with TMPDIR/TMP/TEMP relocated off the MSYS2 tree vs 10 without No. 2/10 vs 4/10, Fisher exact two-tailed p=0.63.
Parallel-make contention 10 full builds at -j1 No. Still 2/10.
Needs a big build around it 250 compiles of sqlite3.c alone, nothing else running No. 10/10 jobs wedged.
One bad source file which TU each wedge hit No. sqlite3.c and quickjs.c both.
Architecture-specific arch of the stuck cc1 No. Both x86_64-linux-cosmo and aarch64-linux-cosmo.
Optimization-dependent -O0 throughout No. All of the above is at -O0.

Environment

cosmocc 4.0.2 (cosmocc-4.0.2.zip, SHA-256 85b8c37a406d862e656ad4ec14be9f6ce474c1b436b9615e91a55208aced3f44), GCC 14.1.0
Host where it reproduces GitHub Actions windows-latest (Windows Server 2025, image windows-2025-vs2026)
Host where it did NOT Windows 11 Pro 26200 desktop - see "the rate is environment-dependent" below
Shell MSYS2 (msystem: MSYS), GNU Make 4.4.1
Optimization -O0

What would help

Anything narrowing where cc1 blocks would beat more sampling from outside:

  • whether the temporary .s output path is implicated - the block is observed while cc1 holds it open;
  • whether this is known behaviour of the Windows port of the GCC cosmocc 4.0.2 ships, or specific to the cosmo build of it (the stuck command line carries -fportcosmo, and #1488's investigation also landed near the portcosmo patch, though that hang is CPU-burning and deterministic while this one is blocked and intermittent);
  • whether a newer cosmocc changes the rate.

Happy to run further arms on request - the sampling method above is short enough that any additional arm can be stood up and answered quickly.

Workaround currently in use

Retrying the build on a stall. make resumes from the objects already completed, so a retry costs a fraction of a build and three attempts leave roughly 3% residual failure. A mitigation, not a fix - it is why a source build of our project on Windows runs nightly rather than on the PR path.