tgc design notes

This page is the canonical design reference for tgc. Earlier blog drafts used “v1 / Phase 2” wording; that split is retired.

Current code: 0.2.3 (see _d_tgc_version()).

Versioning (SemVer pre-1.0)

Version Kind Scope

0.1.0

First prototype

Per-thread private heaps, local collect, remote-free stub. No region API.

0.1.1

Patch

Region API scaffold only (no region collect).

0.2.0

Feature / breaking milestone

Region collect with selective suspend, heap list locks, array append metadata, tests + benchmark harness.

0.2.1

Patch

Sorted-address findBlock O(log n), 32-byte block header (dropped list links), bounded fixpoint mark (skip sweep if no converge).

0.2.2

Patch

Layered pointer lookup (range reject, linear for tiny heaps, binary otherwise), bulk index shifts, tri-state fixpoint marking, full stack/TLS scanning, corrected append metadata, and an explicit remote-free test.

0.2.3 (current)

Patch

Exact-base API conformance, race-safe duplicate-coalescing remote frees, overflow checks, terminating worklist-style marking, serialized region collection, suspension lock barriers, and private-heap-to-region reachability.

0.3.0 (planned)

Opt-in feature

SymGC-backed shared regions via tgcShared:symgc (stub logs; native backend today).

Bump rules (short)

  • Pure bugfix (wrong collect, race, crash) → 0.1.z patch.

  • Non-breaking improvement (perf, better findBlock, docs, additive API with no behavior change) → 0.1.z patch.

  • Breaking change (cross-thread contract, default alloc routing, removing remote-free, region collect that changes pause guarantees) → 0.y.0 minor bump.

  • SymGC + tgc hybrid: not breaking if it is a new opt-in gcopt flag and default gc:tgc matches prior minor; breaking if existing region or private-heap behavior changes without opt-in → bump with the feature that changes defaults.

Problem statement

D isolates data by default (TLS, actor-style std.concurrency) but the default GC uses one process-wide heap. Any collection can stop-the-world pause every registered thread, including @nogc realtime workers that never allocate.

The goal is pause isolation: threads that do not depend on the same GC data should not pause each other.

Target architecture (the actual design)

Per-thread private heap

Every attached thread owns a private arena. Collection scans only that thread’s stack, TLS, roots, and blocks in its private heap. No global STW for a pure local collect.

Partitioned shared regions (core cross-thread model)

Cross-thread data is not modeled as “copy everything” or “remote free on the owning thread’s private heap.” Those are interim or escape-hatch paths only.

The intended model is partitioned shared regions:

  • A region is a GC heap shared by an explicit set of threads (many-to-many).

  • Threads opt in to regions that match their data dependency and lifetime.

  • Collecting region Alpha pauses only threads attached to Alpha, not the whole process.

  • A thread with only a private heap and no shared regions is never paused by another region’s collection.

SymGC hybrid (future, inside gc:tgc)

One registered factory (gc:tgc), not two GCs in one process. Private heaps stay tgc-local; shared regions may use a SymGC-derived allocator/collector backend for the shared slice only. Requires extracting or cooperating with SymGC — not linking gc:sdc and gc:tgc as separate active collectors.

What ships today

0.1.0

  • --DRT-gcopt=gc:tgc

  • Local collect on private per-thread heap

  • Remote-free queue on private heaps (implemented, but interim to the region model)

0.2.0 adds

  • _d_tgc_region_collect with selective thread suspend (thread_suspendList)

  • Per-heap list locks; basic array append metadata

  • Tests: tgc_regions.d; benchmark: tgc_bench.d + tools/tgc-bench/run-benchmarks.ps1

  • Demo page: tgc demo

Pluggable GC and SymGC

druntime selects one GC implementation at process startup (--DRT-gcopt=gc:…). Per-branch mixing of gc:sdc and gc:tgc is not supported without a composite gc:tgc implementation.

Source material on disk

  • Blog article

  • Forum draft: AMDphreak/.issues/forum-tgc-thread-local-gc.md

  • Agent plan: tgc_opt-in_upstream_bfb2d23a.plan.md (machine-local Cursor plans)

Upstream