ReviewOS

zig-utils/zig-js

JSON: append parsed array elements without publication locks

#1007
Closed glennmichael123 opened this 10 days ago ·
10 days ago

Problem

JsonParser.parseArray appends every completed child with the public Object.appendElement path even though the entire parsed graph remains private until parsing succeeds. appendElement always calls lockElements; a default single-owner process pays the enable-gate atomic load per element, while any process that has enabled parallel synchronization takes a full object mutex for these still-unpublished arrays.

The frozen representative_json row parses 2,200 jobs x 90 rounds x 3 array elements = exactly 594,000 private appends per invocation. This is separate from #1006 and follows the unpublished object-field boundary established by #994.

Change boundary

Add an explicit unpublished dense-element append primitive that preserves the GC barrier, indexed-own publication bit, element allocator ownership, append ordering, and exact OOM behavior but does not enter elements_lock. Use it only from JSON array construction before the result can escape. Keep array literals, public Array methods, reviver mutation, Proxy/exotic behavior, and every published/shared append on the existing synchronized path.

Parent: #473.

Completion

  • Pin disabled/enabled lock-counter behavior: unpublished parse construction acquires zero element mutexes even after parallel synchronization is enabled, while the public append remains synchronized.
  • Preserve allocation-failure rollback, nested/deep parse behavior, reviver source records, GC relocation, and no-GIL/TSan safety.
  • Keep JSON Test262 and required VM/tree agreement exact.
  • Run seven alternating exact-parent pairs on both frozen JSON rows plus parse-heavy and stringify-only controls, with exact checksums and no hidden control regression.
  • Land only with measured instruction/CPU improvement or close with the executed negative result.
10 days ago

Landed directly on main in f3639ef.

Result:

  • JSON.parse now prepares dense-element storage once while each array is unreachable, retaining the ordinary per-value GC barrier, indexed-own publication, allocator ownership, and synchronized public mutation path.
  • The frozen representative_json row exercises exactly 594,000 private array appends per invocation.
  • Seven alternating exact-parent pairs preserved checksum 324952086 on both frozen JSON rows and all parse/stringify controls. Retired-instruction ratios were 0.9961x for representative_json, 0.9940x for representative_json_variant, 0.9934x for escaped strings, and 0.9968x for reviver-source parsing; stringify controls remained neutral at 0.9967x and 1.0019x.
  • A separate 9x3 grouped confirmation measured representative_json at 0.98825x retired instructions and 0.97992x process CPU (0.33% / 3.88% paired RSD). The variant measured 0.99596x instructions and CPU-neutral 1.00247x (0.64% / 2.48% RSD). All checksums remained exact.

Qualification:

  • JSON test262: 165/165 pass.
  • Required VM/tree agreement: 165/165 agree.
  • Real-GC JSON units: 33/33, including forced lock counters and exhaustive nested-array allocation failures.
  • Shared no-GIL parser witness under TSan: pass.
  • Full value-layer TSan suite: 122/122 pass.
  • Peak guarded build footprint: 1.96 GB; no job exceeded the 3 GB cap.

Sign in to comment on this issue.