Releases: hale-lang/hale
Release list
v0.10.0 — topology-aware placement + perspectives (live redeploy)
The largest release since v0.9.0 (cumulative since v0.9.2). Two complete multi-PR feature lines, first-class macOS support, and a substrate P0 fix. No source-level breaking changes — all new surface is opt-in.
🗺️ Topology-aware placement (Phase 1 — complete)
Describe the host machine and map loci onto its NUMA/cache/core hierarchy, with memory co-located to the thread. A 4-PR stack; a Linux-only optimization that degrades gracefully (advisory no-op on macOS/other), and costs the runtime nothing unless opted into.
pinned(cores = A..B | A..=B | {a, b, c})— generalize single-core affinity to a cpuset; a range carves out an isolation domain (#168).topology { }block +pinned(node = N)/pinned(l3 = name)— declare the socket → NUMA node → L3 domain → core hierarchy and target any level (#173).- Arena-on-node NUMA co-location — a node-pinned locus allocates its arena on that node via raw
mbind(no libnuma dependency); the "thread + memory" payoff for big-box perf (#174). replicas = K— fan a locus into K single-threaded instances, one per core in the range. Parallelism as more units, each still single-threaded — every lock-free / devirtualization invariant survives (#175).
🔄 Perspectives as live redeploy (Phase 2–3)
A perspective is now a first-class, live-rebindable handle to a contract: program against a stable ABI, and swap the implementation behind it at pointer-flip cost — no restart, no global pause.
- Contract +
serves+ global slot + dispatch — the swappable 1-1 indirection (#172). reperspective— the live redeploy: load the new impl, flip the slot, drain the old (#176).- Bus surface in the contract + conformance — bus subscribe/publish edges are part of the swappable ABI, preserved across a swap (#177), with subscriptions re-pointed at runtime (#180).
- State-preserving swap / migration (Phase 3) — layout-identity swaps repoint code at the existing arena (zero data movement); a changed footprint runs a
migrate(#179).
🍎 macOS (Apple Silicon) support — phase 1
The runtime now builds and runs on macOS 14 (Apple Silicon), with async_io gated behind a clear compile diagnostic pending a kqueue backend.
- Core port; Linux-only
SO_PRIORITY/IP_PKTINFOguarded,libhale_ts_shim.ashipped;clock_nanosleepshim viananosleep; install docs rewritten for prebuilt binaries.
🧰 Forms, CLI & release
@form(lru_cache)— bounded LRU cache form; batched hashmap iteration (64-entry batches).hale test— discover + run*_test.hl(spec/testing.md).- Reproducible, self-contained Linux release via Docker.
🐛 Fixes & hardening
- Anchor-retirement freelist P0 (#182) — a String-keyed
@form(hashmap)aliases the key clone with itsindexed_byvalue field, which was retired twice → self-linked freelist node → SEGV under multi-key churn. Fixed by deduping the retire; the underlying leak fix is preserved. - Bus view-handler correctness —
__hwrap_view-load gating + view-aggregate handling inlen()/ dispatch. - DWARF debug-info repair sweep — line-0 / synthesized-epilogue
!dbglocations. - Rust/C++ competitiveness pass — memory model, bounded collections, typecheck M3, build latency (#167).
- Test de-flaking under nextest concurrency (bubble / udp / cross-pool) + spec / mdBook refresh.
What's Changed
- Rust/C++ competitiveness: memory model, perf-to-parity, bounded collections, typecheck M3, build latency by @rileyr in #167
- feat(placement): pinned(cores = A..B / {…}) cpuset affinity (topology 1a) by @rileyr in #168
- feat(placement): topology { } + pinned(node = N) / pinned(l3 = name) (topology 1b) by @rileyr in #173
- feat(runtime): arena-on-node NUMA memory co-location for pinned(node/l3) by @rileyr in #174
- feat(placement): replicas = K — fan a locus into K single-threaded instances (topology 1c) by @rileyr in #175
- feat(perspective): contract + serves + global slot + dispatch (perspectives 2a) by @rileyr in #172
- feat(perspective): reperspective — the live redeploy (perspectives 2b) by @rileyr in #176
- feat(perspective): bus surface in the contract + conformance (perspectives 2c) by @rileyr in #177
- test(udp): kill the loopback round-trip flake under nextest concurrency by @rileyr in #178
- feat(perspective): state-preserving reperspective swap (perspectives Phase 3) by @rileyr in #179
- feat(perspective): bus subscriptions swap on reperspective (perspectives 2c runtime) by @rileyr in #180
- test(bubble): retry delivery-expecting bubble assertions under CI concurrency by @rileyr in #181
- fix(runtime): dedup anchor-retirement retire so aliased key/value blob isn't double-freed by @rileyr in #182
Full Changelog: v0.9.2...v0.10.0
What's Changed
- Rust/C++ competitiveness: memory model, perf-to-parity, bounded collections, typecheck M3, build latency by @rileyr in #167
- feat(placement): pinned(cores = A..B / {…}) cpuset affinity (topology 1a) by @rileyr in #168
- feat(placement): topology { } + pinned(node = N) / pinned(l3 = name) (topology 1b) by @rileyr in #173
- feat(runtime): arena-on-node NUMA memory co-location for pinned(node/l3) by @rileyr in #174
- feat(placement): replicas = K — fan a locus into K single-threaded instances (topology 1c) by @rileyr in #175
- feat(perspective): contract + serves + global slot + dispatch (perspectives 2a) by @rileyr in #172
- feat(perspective): reperspective — the live redeploy (perspectives 2b) by @rileyr in #176
- feat(perspective): bus surface in the contract + conformance (perspectives 2c) by @rileyr in #177
- test(udp): kill the loopback round-trip flake under nextest concurrency by @rileyr in #178
- feat(perspective): state-preserving reperspective swap (perspectives Phase 3) by @rileyr in #179
- feat(perspective): bus subscriptions swap on reperspective (perspectives 2c runtime) by @rileyr in #180
- test(bubble): retry delivery-expecting bubble assertions under CI concurrency by @rileyr in #181
- fix(runtime): dedup anchor-retirement retire so aliased key/value blob isn't double-freed by @rileyr in #182
Full Changelog: v0.9.2...v0.10.0
v0.9.2
v0.9.1
v0.9.0
What's Changed
- types: R1 escape-awareness for --warn-unbounded-alloc + precision scope (GH #18 item 1) by @rileyr in #120
- notes: drop R3 (lifetime-awareness) from the precision plan — unsound by @rileyr in #121
- unbounded-alloc: name bounded fixes in the warning + fix moving-average in place by @rileyr in #122
- examples: fitter updates the kernel in place (clears the last unbounded-alloc warnings) by @rileyr in #123
- cli: turn --warn-unbounded-alloc on by default (GH #18 item 1) by @rileyr in #124
- notes: scope bounded bus queue + backpressure (GH #125) by @rileyr in #126
- runtime: bound the cooperative bus queue + backpressure (GH #125 v1) by @rileyr in #127
- runtime: cross-pool (any->pinned) bus backpressure via bounded mailbox (GH #125) by @rileyr in #128
- post-audit hardening: WS0–WS5 (verification + 3 real fixes + docs) by @rileyr in #129
- fix(codegen): locus-field reassignment is a lifecycle transition (WS1#4) by @rileyr in #130
- io::tls recv/send timeouts + recv-timeout sentinel (WsClient liveness, Part A) by @rileyr in #132
- notes: corrected verdict on TLS concurrent-recv starvation + pond liveness handoff by @rileyr in #131
- fix(runtime): close use-after-free race in the TLS handle table by @rileyr in #133
- fix(runtime): serialize the shared bus-payload arena's bump by @rileyr in #134
- feat(stdlib): std::io::tcp::set_nodelay + TCP_NODELAY constant by @rileyr in #135
- feat(diag): alloc/syscall gate counters (std::diag) by @rileyr in #137
- feat(stdlib): recv_stamped_into — kernel RX timestamps (one recvmsg) by @rileyr in #138
- feat(stdlib): std::bytes find_byte + block-XOR masking by @rileyr in #139
- feat(ring_layout): record_header framing + post_copy recheck by @rileyr in #140
- feat(stdlib): std::io::MirrorRing — double-mmap wrap-free buffer by @rileyr in #141
- test(io): recv_into zero-alloc audit + gate (fast-protocol-I/O #6) by @rileyr in #142
- feat(ring_layout): in-band record-header field delivery (std::shm::last_record_*) by @rileyr in #143
- docs(notes): fast-protocol-I/O substrate scoping note (shipped) by @rileyr in #144
- chore: neutralize external/internal proper-noun references in foreign-ring work by @rileyr in #145
- feat(stdlib): TLS fast-path siblings — set_nodelay + recv_stamped_into by @rileyr in #146
- fix(tls): recv_stamped kernel timestamp via recvmsg cmsg (not SIOCGSTAMPNS) by @rileyr in #147
- fix(shm): post_copy torn-read guard resyncs at cap-S, not a full lap by @rileyr in #148
- verify: GenMC model for the arena subregion-slot lock (#18 item 2) by @rileyr in #150
- docs: sync spec + book with the foreign-ring + lifecycle surface by @rileyr in #151
- feat: WebAssembly compilation target (Phases 0–2) by @rileyr in #152
- feat(wasm): host interface — @export/entry-inversion + persistent locus by @rileyr in #153
- fix(wasm): heap-allocate the @export-locus singleton (was a dangling stack alloca) by @rileyr in #154
- feat(stdlib): expose std::io::tcp::__set_recv_timeout_ns (bounded accept) by @rileyr in #155
- fix(wasm): silence -Wbuiltin-requires-header on the freestanding shim by @rileyr in #156
- feat(stdlib): std::math::round / trunc — Float→Int with rounding mode by @rileyr in #157
- fix(wasm): real snprintf in the libc shim — String+Int no longer emits empty by @rileyr in #158
- fix(wasm): Decimal i128 builtins + @ffi("js") Int↔number marshaling by @rileyr in #159
- docs: sync spec + book with the stdlib / WASM / syntax surface by @rileyr in #160
- feat(alloc): memory-bound proofs — opt-in gating + capacity-aware bounding (GH #18 item 1) by @rileyr in #166
Full Changelog: v0.8.3...v0.9.0
v0.8.3 — verification track, SHM-ring interop, fast JSON
The largest release since v0.8.0 (cumulative since v0.8.2). Four headline arcs, no source-level breaking changes.
🔍 Compile-time verification track (GitHub #18)
Six candidate analyses, addressed — canonical catalog in spec/verification.md.
- Bus-graph checks (item 4) — default-on: blocking-call detection, orphan topics, bus cycles + re-entrant sync-deadlock, backpressure, subject type-mismatch.
- Race-completeness (item 2) — a GenMC model-checking CI gate over the lockfree hashmap, pinned-locus mailbox, and cooperative-pool bus queue under all C11 interleavings.
- Memory-bound proofs (item 1) — opt-in
hale check --warn-unbounded-alloc. Per-method allocation summary + call-graph escape/loop dataflow, an empirically-validated reclamation model (which corrected the spec), a bound solver, call-result escape tagging, and loop-ranking that proveswhile v < Nconst counters bounded. Off-default deliberately, pending an@unboundedescape valve. - Resource-budget tracking (item 5) — opt-in: counts (threads / pools / subjects / fds), a CI ceiling gate (
--check-resource-budget budget.toml), and fd-leak detection (--warn-resource-leak). - Closure-assertion lifting (item 3) — scoped & deliberately parked (the tractable case is already handled by typecheck).
🔗 Binary shared-memory-ring interop
Read/write externally-defined SHM rings by declaring their layout — no hand-written FFI.
std::bytesbinary packing — bounds-checked LE/BE readers +BytesBuilderwriters.ring_layoutdeclaration +shm_ring(..., layout: N)binding; read-only consumer, producer, and conformance checks.- Raw
BytesViewpayload mode for heterogeneous rings; Go-style struct field tags + repr-tagged field accessors; a zero-copy reserve/commit write surface.
⚡ JSON performance
Generated codecs from json: tags (Tier 2) → SIMD/AVX2 cursors (Tier 3) → inlined leaf primitives. A representative parse went ~291 ms → ~58 ms — within range of V8.
🧰 Standard library & runtime
std::term(is_tty,size,RawMode) + raw byte I/O (std::io::stdout::write_bytes,std::io::stdin::read_byte) — terminal hygiene with no vendored FFI.- Tree-walking interpreter retired —
hale runcompiles + execs via codegen. BytesBuilder.append_str; ECDSA P-256fallible(CryptoError); stale-view panic viaexit()(atexit-safe).
Plus the earlier v0.8.3 language-surface work (CQRS no-locus-return, resets_per_epoch windowed closures, nested-long-running-child rejection) and a codegen reorg.
Full details: see CHANGELOG.md.
What's Changed
- Refactor/codegen model org by @rileyr in #22
- Per-child reclamation for accept'd children (close the daemon leak) by @rileyr in #23
- fix(runtime): poke async pool wake_fd on cross-pool enqueue (on_out starvation) by @rileyr in #24
- fix(desugar): keep cross-pool publishes on the bus (intra-locus opt pool-safety) by @rileyr in #25
- feat(typecheck): gate terminate (locus-only) and release (needs accept) by @rileyr in #26
- feat(reclaim): unify per-child teardown spine + cascade + handler-terminate by @rileyr in #27
- fix(runtime): clean shutdown of classic-pool blocking-accept servers (refstore SIGSEGV) by @rileyr in #28
- feat: self.children.count / .is_empty summary sugar (F.11) by @rileyr in #29
- docs(runtime): resolve Item B (coop→pinned mailbox) — sequencing, not a lost wakeup by @rileyr in #30
- docs: restructure the book + refresh the pond library catalog by @rileyr in #31
- harden: compiled-corpus ASAN oracle + the three leaks it found by @rileyr in #32
- fix(codegen): resolve enum names to Enum, not TypeRef by @rileyr in #33
- perf+ci: cache compiled runtime objects; free runner disk; bound hung tests by @rileyr in #34
- typecheck: reject a dead bus receiver (non-main cooperative subscriber) by @rileyr in #35
- stdlib: std::text::base64::url_encode (RFC 4648 §5, unpadded) by @rileyr in #36
- stdlib: std::crypto ECDSA P-256 (ES256) sign + verify by @rileyr in #37
- typecheck: warn on a blocking syscall in a cooperative-pool run() by @rileyr in #38
- docs+diag: close fathom bus-receipt handoff asks #3-6 by @rileyr in #39
- typecheck: correct the dead-receiver check — require a blocking run() by @rileyr in #40
- hale run compiles + execs (no interpreter) — retire slice 1 by @rileyr in #41
- Delete the hale-runtime interpreter crate — retire slice 2 by @rileyr in #42
- crypto: ecdsa_p256_sign gains a fallible(CryptoError) form by @rileyr in #43
- Interprocedural blocking-call detection (warning path) by @rileyr in #44
- Bus-graph orphan-topic check (GH #18 #4, PR A) by @rileyr in #45
- Bus cycle warning + re-entrant sync-deadlock error (GH #18 #4, PR B) by @rileyr in #46
- spec: add verification.md — canonical static-check catalog by @rileyr in #47
- Bus backpressure check (GH #18 #4) by @rileyr in #48
- Bus subject type-mismatch check (GH #18 #4 — completes item 4) by @rileyr in #49
- Race-completeness PoC: GenMC model of the lockfree hashmap (GH #18 #2) by @rileyr in #50
- Mailbox GenMC model (GH #18 #2, second primitive) by @rileyr in #51
- CI: GenMC race-completeness gate (GH #18 #2) by @rileyr in #52
- Bus queue GenMC model (GH #18 #2, third primitive) by @rileyr in #53
- notes: binary SHM-ring interop design proposal (ring_layout + std::bytes pack) by @rileyr in #54
- stdlib: std::bytes binary-pack readers (shm-ring-interop Proposal A, M1) by @rileyr in #55
- stdlib: BytesBuilder binary-pack writers (shm-ring-interop Proposal A, M2) by @rileyr in #56
- ring_layout declaration (shm-ring-interop Proposal B, PR1) by @rileyr in #57
- shm_ring layout: binding kwarg (shm-ring-interop Proposal B, PR2) by @rileyr in #58
- codegen+runtime: foreign-ring read-only consumer (Proposal B, PR3) by @rileyr in #59
- types: ring_layout↔payload conformance checks (Proposal B) by @rileyr in #60
- codegen+runtime: foreign-ring producer (Proposal B, M3a) by @rileyr in #61
- shm-ring-interop: genericize the foreign-ring naming by @rileyr in #62
- codegen: fix runtime-object temp-path race in parallel builds by @rileyr in #64
- experiments: foreign-ring throughput microbench (Proposal B) by @rileyr in #63
- runtime: incremental wrapped-offset in the foreign-ring hot loops by @rileyr in #65
- spec: catalog the ring_layout conformance checks in verification.md by @rileyr in #66
- shm-ring-interop: close three OOB holes at the foreign-producer boundary by @rileyr in #67
- ci: run the foreign-ring UBSan suite in the asan job (no extra build) by @rileyr in #68
- ci: build-and-run per partition (drop the serial build+archive job) by @rileyr in #69
- tests: fix bytes_pack_read parallel-build-path flake (#67 follow-up) by @rileyr in #70
- docs: highlight Hale code blocks (register
halewith highlight.js) by @rileyr in #71 - codegen+runtime: raw BytesView payload mode for ring_layout consumer by @rileyr in #72
- readme: syntax-highlighted SVG for the hero Hale snippet by @rileyr in #73
- syntax: single source for the keyword list; generate the highlighters by @rileyr in #74
- ring_layout: slots framing — read the native LotusRing via the abstraction by @rileyr in #75
- cli+syntax: name the right file + line in multi-file diagnostics by @rileyr in #76
- codegen: BytesView producer path for foreign ring_layout by @rileyr in #77
- runtime: reserve/commit split for zero-copy ring writes (A1, stage 1) by @rileyr in #78
- codegen+syntax: zero-copy ring write surface (A1, stage 2+3) by @rileyr in #79
- syntax: Go-style struct field tags (parse + store) by @rileyr in #80
- codegen+syntax: repr-tagged field accessors (Proposal A′) by @rileyr in #81
- types: typecheck guard for repr-tagged field accessors (A′ follow-up) by @rileyr in htt...
v0.8.2
What's Changed
- bus: udp:// transport (unicast + multicast) by @rileyr in #4
- docs: document udp:// bus transport by @rileyr in #5
- bus: spill-to-heap payloads + jumbo-aware UDP transport by @rileyr in #6
- bus: bound-check deserialize length prefixes (closes fathom priceview crash) by @rileyr in #7
- bus udp: bind multicast receivers to the group address (fix crosstalk) by @rileyr in #8
- std::crypto: add crc32(b: Bytes) -> Int (#12) by @rileyr in #14
- std::io::tcp: add set_recv_timeout / set_send_timeout (#13) by @rileyr in #15
- std::io::tcp::Stream: bus-routed I/O observability (#10 partial) by @rileyr in #16
- std::http::Server: bus-routed observability (closes #10 modulo TLS) by @rileyr in #17
- bus udp: array-of-pointers for remote entries (fix listen+connect SIGSEGV) by @rileyr in #19
- release: drop macos-13 (Intel Mac) from the build matrix by @rileyr in #20
- v0.8.2: docs cleanup + 9-PR bug-fix bundle by @rileyr in #21
Full Changelog: v0.8.1...v0.8.2