From c64369db26c16992fd664475cf8934d05286967d Mon Sep 17 00:00:00 2001 From: Kacy Fortner Date: Fri, 17 Jul 2026 17:26:35 +0000 Subject: [PATCH] document the recent language and stdlib changes - grammar.ebnf: the threadlocal keyword and the threadlocal binding form - concurrency.md: note that std.json/std.toml/std.io lean on threadlocal globals, which is what makes them safe to use from many tasks at once - limitations.md: the weak-field-read box leak is fixed for the common consumers (!= none / == none / .value()); only the indexed-collection read still synthesizes an unreclaimed optional - README: add std.env to the stdlib module list (it now has typed readers) performance.md already covers the freelist and connection pool; the grammar already documented numeric underscore separators, which now actually work. --- README.md | 2 +- docs/concurrency.md | 6 ++++++ docs/grammar.ebnf | 5 +++-- docs/limitations.md | 16 +++++++++------- 4 files changed, 19 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e0541036..93f4b1c5 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ websocket, tls, sse), databases (sql, postgres, mysql, redis — pure-pith wire protocols with tls, prepared statements, and pooling), data (json, toml, csv, config, table), bytes and crypto (hash, checksum, encoding, crypto, bits, binary), compression and archives (gzip/zlib, tar, zip), -text (regex, scanner, fmt), app plumbing (log, metrics, cli, testing, +text (regex, scanner, fmt), app plumbing (log, metrics, cli, env, testing, diagnostic, time, datetime, rand, uuid, math), and lazy iterators (std.iter). diff --git a/docs/concurrency.md b/docs/concurrency.md index ca40fa78..a4e25f90 100644 --- a/docs/concurrency.md +++ b/docs/concurrency.md @@ -163,6 +163,12 @@ set of `threadlocal` globals small and the values scratch-sized. use it for state that is genuinely per-task; a value shared *across* tasks still belongs in a struct or behind a channel. +this is what lets the std parsers and buffers be used from many tasks at +once: `std.json` and `std.toml` keep their parse arena in `threadlocal` +globals, and `std.io`'s readers/writers keep their per-instance state the +same way, so two tasks parsing or buffering concurrently each work in +their own state with no lock and no race. + things that are still intentionally explicit or still growing: - task cancellation is cooperative, not forceful diff --git a/docs/grammar.ebnf b/docs/grammar.ebnf index e43061d5..c41523fc 100644 --- a/docs/grammar.ebnf +++ b/docs/grammar.ebnf @@ -76,7 +76,7 @@ KEYWORD = "fn" | "if" | "elif" | "else" | "for" | "in" | "while" | "match" | "select" | "default" | "timeout" | "return" | "fail" | "break" | "continue" | "struct" | "enum" | "interface" | "impl" | "type" - | "pub" | "mut" | "ref" | "weak" + | "pub" | "mut" | "ref" | "weak" | "threadlocal" | "import" | "from" | "as" | "self" | "true" | "false" | "none" | "and" | "or" | "not" | "catch" @@ -98,6 +98,7 @@ import_list = import_name , { "," , import_name } import_name = IDENT , [ "as" , IDENT ] ; +(* a top-level binding may be marked `threadlocal` for per-os-thread storage *) top_level_decl = [ "pub" ] , ( fn_decl | struct_decl | enum_decl @@ -105,7 +106,7 @@ top_level_decl = [ "pub" ] , ( fn_decl | impl_decl | type_alias | test_decl - | binding ) , NEWLINE ; + | [ "threadlocal" ] , binding ) , NEWLINE ; test_decl = "test" , STRING_LIT , ":" , block ; diff --git a/docs/limitations.md b/docs/limitations.md index 10a969e4..e704696d 100644 --- a/docs/limitations.md +++ b/docs/limitations.md @@ -94,13 +94,15 @@ correctness story: cannot yet be weak. container element removal leaks until the container dies, and error-path early returns skip cleanup. all are bounded-leak by design — the discipline never produces a dangling pointer in exchange. -- reading an optional that is synthesized rather than stored leaks a small - optional box per read. this covers a `weak` field read and an indexed read - of a collection whose element type is a struct (`items[i]` where `items` is - `List[Node]`): both build a fresh optional to carry the liveness/`some` - result, and that box is not reclaimed. a stored optional field read does not - synthesize and does not leak. the leak is bounded per read and never - dangles; releasing synthesized optional temporaries is a known follow-up. +- reading an optional that is synthesized rather than stored can leak a small + optional box per read. a `weak` field read builds a fresh optional to carry + the liveness result; the common consumers — `!= none` / `== none` and + `.value()` — now free that shell once the value is extracted, so the common + idiom no longer leaks. an indexed read of a collection whose element type is a + struct (`items[i]` where `items` is `List[Node]`) still synthesizes an optional + that is not reclaimed. a stored optional field read does not synthesize and + does not leak. the leak is bounded per read and never dangles; reclaiming the + remaining synthesized-optional temporaries is a known follow-up. - a handful of edge cases logged during bring-up (cross-module float returns, cross-module map reads, set codegen, negative float literals like `-1.0`) were re-checked and all pass; they are now pinned by regression tests