Skip to content

build std's shared lookup tables at module load, not on first use - #624

Merged
kacy merged 1 commit into
mainfrom
synchronize-std-lazy-tables
Jul 30, 2026
Merged

build std's shared lookup tables at module load, not on first use#624
kacy merged 1 commit into
mainfrom
synchronize-std-lazy-tables

Conversation

@kacy

@kacy kacy commented Jul 30, 2026

Copy link
Copy Markdown
Owner

summary

std/hash.pith, std/net/http2/hpack.pith and std/net/http2/huffman.pith
each filled a shared lookup table on first use behind a mut flag, and set
the flag before filling. with green the default on linux, two tasks on two
workers can reach a cold table together — the second sees the flag set,
returns, and reads a partially built table. for the sha round constants that
is a wrong digest or an out-of-bounds index; concurrent push also races the
list's own length and buffer.

all three tables are plain data, so they are module constants now, built at
module load — before main's body and therefore before any task exists. that
deletes the flag, the init function and the window instead of locking them.
the sha tables shed 255 lines of push calls for the literal they always
were.

what was tested

the crypto known-answer tests, make test on both backends, both corpora,
make run-examples, make docsite-check, and grpc_chat + grpc_reflect,
which exercise hpack and huffman per request. a new case has sixteen tasks
spin on a barrier and hit all three tables at the same instant, asserting a
single agreed answer; identical under green default, one worker, and
PITH_GREEN=0.

notes

the race could not be made to fire on this two-core box: wake affinity keeps
channel-woken tasks on the same worker, so they serialize. the new case
therefore passes before and after — it pins the intent and fails if a table
returns to lazy construction. test_h2_connection_reuse flaked once during a
loaded battery run; it passes 6/6 under artificial load on this branch and
6/6 on main, so it is the known load flake rather than anything here.

the sha round constants, the hpack static table and the huffman decode map
were each filled in on first use behind an unsynchronized flag, and the flag
was set before the filling started. green is the default on linux now, so two
tasks on two workers can reach a cold table together: the second sees the flag
already set, returns, and reads a half-built table. for the sha tables that
means a wrong digest or an index past the end, and concurrent pushes race the
list's own length and buffer besides.

all three are plain data, so they are module constants now, built while the
module loads — before main's body runs and therefore before any task exists.
that removes the flag, the init function, and the window, rather than guarding
them: there is no first use to synchronize when the table is already complete.
the sha tables also lose 255 lines of push calls in favour of the literal they
always were.

i could not make the race fire on this two-core box — wake affinity keeps
channel-woken tasks on one worker, so they serialize — and the new case will
pass before and after for that reason. it is here to pin the intent and to
fail if a table ever goes back to being built lazily.
@kacy
kacy merged commit 76c68af into main Jul 30, 2026
2 checks passed
@kacy
kacy deleted the synchronize-std-lazy-tables branch July 30, 2026 13:12
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.

1 participant