Repository navigation
Conversation
… compile The HTTP thread compiles a request's grammar to refuse a bad one with a 400. It used to free that compile and let the lane thread compile again under the compiler's mutex: usually a cache hit, but the lane thread then waits while another request's schema compiles. The request now carries the compiled handle (api.Structure.compiled); the lane host builds the matcher from it without a compile or the lock, and the server frees it once the engine has finished (Server.release, which replaces chat.release). A required tool's grammar that response_format replaces is freed at once, and a refused request frees what it compiled. Without a handle the lane host compiles as before.
This was referenced Oct 26, 2026
Owner
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Branch
hostfix-grammar, commitdc84aa99b(on main c0c7bd2).The HTTP thread compiles a request's grammar so it can refuse a bad one with a 400 (
server.zigcompiledStructure). It then freed that compile. The lane thread compiled the same text again underCompiler.mutex(lane_stream.zig:61→lane_host.zig:393→lanes/grammar.zig:81). That compile is usually an xgrammar cache hit. The cost is the lock: the lane thread, which runs every stream's rounds, waits while another request's schema compiles on its HTTP thread.Now
api.Structurecarries the compiled handle (compiled). The lane host builds the matcher from it withCompiler.constrained, without a compile or the lock. The server frees the handle once the engine has sentfinished:Server.release(preparing, request)replaceschat.releaseand frees the grammar. It is called fromgenerate's defer (after the drain) and wherever a prepared request is dropped (compact, cortheon, imprint, openai).response_formatreplaces is freed at once.checkFeaturesand inprepare).A request without a handle (other engines, or tests) is compiled as before.
Tests:
lanes/grammar_handoff_test.zig: the handle gives no second compile, and the masks along a token walk match a fresh compile. A second grammar's masks differ, so the comparison is not vacuous.lane_host_grammar_test.zig: a request through the lane host with a handle compiles 0 times on the lane thread, and its masked reply matches the compile path's. If the lane host ignores the handle, the test fails (2 compiles, not 1).tool_required_test.zig: each compile is freed exactly once, including the replaced grammar and a refused one.lanes/grammar_fake.zig) counts compiles and frees.Gates: full
zig build test -Dnvccfrom the repo root exits 0. Lean: 0 new problems. Served on GB10 (Qwen3.6-35B-A3B MLX 4-bit + MTP, xgrammar v0.2.8, 8 lanes):Overlap: #1295, #1290, #1281, #1268, #1254, #1239, #1235 and #1215 also touch
server.zig,chat.zig,engine_api.zigorlane_host.zig.Metal (M-series Mac, zig 0.17.0):
zig build testpasses (RC=0). A served Qwen3.5-2B smoke run (strict JSON schema solo and two at once, tool_choice required, a three-turn chat with turn 3 repeated, a plain prompt, temperature 0) is byte-identical to main c0c7bd2.