Skip to content

Add ghc-throttle: transparent GHC concurrency limiter#178

Closed
angerman wants to merge 1 commit into
stable-ghc-9.14from
feat/ghc-throttle
Closed

Add ghc-throttle: transparent GHC concurrency limiter#178
angerman wants to merge 1 commit into
stable-ghc-9.14from
feat/ghc-throttle

Conversation

@angerman

@angerman angerman commented Mar 18, 2026

Copy link
Copy Markdown

Summary

Adds ghc-throttle, a transparent drop-in wrapper that limits concurrent GHC processes to prevent OOM kills and swap thrashing during parallel builds.

POSIX (Linux, macOS, FreeBSD): flock() + exec() — the wrapper acquires a file lock, then exec()s the real GHC. After exec, there is zero wrapper overhead (the process IS GHC). When GHC exits, the kernel releases the lock automatically. Crash-safe by design.

Windows: Named mutexes (Local\ghc-throttle-slot-N) + CreateProcess. The wrapper holds the mutex while the child GHC runs. Mutexes are auto-released on process exit.

Key features

Feature Details
Transparent Build systems see a normal GHC binary — no changes needed
Smart bypass --version, --info, --print-*, --supported-extensions, --show-options skip throttling
-jsem aware Skips throttling when GHC's jobserver semaphore is active
Cross-platform Linux (/proc/self/exe), macOS (_NSGetExecutablePath), FreeBSD (sysctl KERN_PROC_PATHNAME), Windows (GetModuleFileNameA)
Crash-safe Kernel auto-releases flock/mutex on exit — no cleanup needed
Recursion detection GHC_THROTTLE_ACTIVE sentinel prevents infinite loops
Zero overhead POSIX: after exec(), wrapper is gone. Windows: minimal CreateProcess wrapper

GHC discovery (priority order)

  1. GHC_THROTTLE_GHC environment variable (explicit path)
  2. <self>.real suffix (e.g., ghc.real next to ghc)
  3. PATH search with self-exclusion (inode comparison on POSIX, case-insensitive path on Windows)

Slot acquisition

  • Fast path: Non-blocking scan across N lock files/mutexes
  • Slow path: Block on slot PID % N (distributes waiters, prevents thundering herd on slot 0)
  • Default concurrency: max(1, min(ncpus/2, 256)), override with GHC_THROTTLE_JOBS

Usage

make ghc-throttle
export GHC_THROTTLE_GHC=$(which ghc-9.8.4)
export GHC_THROTTLE_JOBS=4
make CABAL_ARGS="--with-compiler=$PWD/_build/bin/ghc-throttle" stage1

Files

File Lines Description
utils/ghc-throttle/ghc-throttle.c ~480 POSIX wrapper (Linux, macOS, FreeBSD)
utils/ghc-throttle/ghc-throttle-win.c ~490 Windows wrapper (named mutexes + CreateProcess)
utils/ghc-throttle/ghc-throttle-status.c ~400 POSIX status monitor (PID reporting on Linux via /proc/locks)
utils/ghc-throttle/ghc-throttle-status-win.c ~170 Windows status monitor (no PID reporting — Windows limitation)
utils/ghc-throttle/Makefile ~45 Standalone build
Makefile ~30 Integration targets + clean

Environment variables

Variable Default Description
GHC_THROTTLE_GHC auto-detect Path to the real GHC binary
GHC_THROTTLE_JOBS ncpus / 2 Max concurrent GHC processes
GHC_THROTTLE_DIR /tmp/ghc-throttle-$UID Lock directory (POSIX only)
GHC_THROTTLE_DEBUG unset Set to 1 for diagnostic output
GHC_THROTTLE_ACTIVE unset Recursion detection sentinel (internal)

Comparison with -jsem

Aspect -jsem ghc-throttle
Build system changes Requires cabal support None — transparent
Scope Threads within one GHC Number of concurrent GHCs
Overhead Runtime scheduler in GHC Zero after exec
Crash safety Semaphore can leak flock/mutex auto-releases
Orthogonal to -j? Replaces -j Yes — controls concurrent GHCs, not threads within

Test plan

  • All POSIX sources compile cleanly with cc -O2 -Wall -Wextra -pedantic -Werror on macOS
  • Clean clang-tidy analysis (no actionable warnings)
  • ghc-throttle --version prints GHC version (with bypass)
  • 4 concurrent invocations with JOBS=2 correctly limits to 2 parallel
  • SIGKILL releases slot immediately (no deadlock)
  • ghc-throttle-status correctly reports held/free slots with PID info
  • ghc-throttle-status --help prints usage
  • Recursion detection works (GHC_THROTTLE_ACTIVE sentinel)
  • 22 rounds of Copilot automated review — all actionable issues addressed

@angerman angerman force-pushed the feat/ghc-throttle branch 2 times, most recently from 8f92126 to 37e2bcd Compare March 19, 2026 01:05
@angerman angerman requested a review from Copilot March 19, 2026 01:32

This comment was marked as outdated.

@angerman angerman force-pushed the feat/ghc-throttle branch from 37e2bcd to fac0d48 Compare March 19, 2026 05:40
@angerman angerman requested a review from Copilot March 19, 2026 05:42

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 05:53

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 06:23

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 06:39

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 06:51

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 07:03

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 07:13

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 07:26

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 07:34

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 07:41

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 07:51

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 08:00

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 08:07

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 08:19

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 08:29

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 08:39

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 08:50

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 09:03

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 09:15

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 09:27

This comment was marked as outdated.

@angerman angerman requested a review from Copilot March 19, 2026 09:39

This comment was marked as outdated.

@angerman angerman force-pushed the feat/ghc-throttle branch from 8f374ac to 6e220f0 Compare March 19, 2026 10:49
@angerman

angerman commented Apr 4, 2026

Copy link
Copy Markdown
Author

Depends on #180

A drop-in wrapper that limits concurrent GHC processes using flock()
+ exec() on POSIX and named mutexes + CreateProcess on Windows.

After acquiring a concurrency slot, the POSIX wrapper exec()s the real
GHC — zero runtime overhead. When GHC exits (normally or via crash),
the kernel releases the flock automatically. The Windows wrapper holds
a named mutex while the child runs via CreateProcess.

Key features:
- Transparent to build systems (looks like a normal GHC binary)
- Smart bypass for query flags (--version, --info, --print-*, etc.)
- -jsem aware (skips throttling when GHC's jobserver is active)
- Cross-platform: Linux, macOS, FreeBSD (flock+exec), Windows (mutexes)
- Crash-safe by design (kernel auto-releases locks/mutexes on exit)
- Recursion detection via GHC_THROTTLE_ACTIVE sentinel

Includes ghc-throttle-status companion tool for monitoring slot usage
with PID-of-holder reporting on Linux (via /proc/locks).

Files:
- utils/ghc-throttle/ghc-throttle.c       — POSIX wrapper
- utils/ghc-throttle/ghc-throttle-win.c   — Windows wrapper
- utils/ghc-throttle/ghc-throttle-status.c     — POSIX status monitor
- utils/ghc-throttle/ghc-throttle-status-win.c — Windows status monitor
- utils/ghc-throttle/Makefile              — standalone build
- Makefile                                 — integration targets

Copyright: Moritz Angermann <moritz.angermann@iohk.io>
License: Apache-2.0
@angerman angerman force-pushed the feat/ghc-throttle branch from 6e220f0 to adeea3d Compare April 5, 2026 08:53
@angerman

angerman commented Apr 6, 2026

Copy link
Copy Markdown
Author

While this works, it's not really competitive with -jsem at higher concurrency, as jsem allows for more fine control between the ghcs.

@angerman angerman closed this Apr 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants