fix(parsers): serialise tree-sitter query execution across threads - #1593
Merged
Merged
Conversation
…1370) Building a Query and running a QueryCursor concurrently on different languages crashes the native extension. It is a Fatal Python error: Segmentation fault, not an exception, so nothing above can catch it or retry — the reported traceback shows three threads inside execute_query at once, on TypeScript, Python and HTML grammars. tree_sitter_manager locked its language *cache* but nothing guarded query execution, and cgc index runs parsers on a thread pool. The lock is process-wide rather than per-language on purpose: the reports involve different languages crashing together, so a per-language lock would not have prevented them. Cost is negligible because only the query step is serialised — parser.parse (tree construction) and all Python-level work stay parallel: 120 parses, 8 threads, mixed Python/TypeScript with lock: 3.48s median of 3 without: 3.45s median of 3 Honest limitation: I could not reproduce the segfault on this machine — 400 concurrent mixed-language parses across 16 threads survived five runs unlocked. The crash is timing- and version-dependent, so the tests pin the guard (no two threads inside the query body) rather than the crash, which cannot be asserted on from inside the process that takes it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Contributor
|
Hi! 👋 Join our CodeGraphContext Discord channel to collaborate: https://discord.gg/dR4QY32uYQ |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
🔍 PR Code Graph Analysisfix(parsers): serialise tree-sitter query execution across threads (#1593) 📊 Interactive VisualizationView the blast radius graph: PR Reviewer Dashboard 📦 ArtifactsThe graph JSON has been uploaded as a build artifact: Generated by CodeGraphContext using FalkorDB Lite |
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.
Fixes #1370. Full suite: 1157 passed, 7 skipped.
The bug
tree_sitter_managerlocked its language cache (_cache_lock) but nothing guarded query execution:cgc indexruns parsers on a thread pool, so several threads reach this concurrently on different grammars. #1370's traceback shows exactly that — three threads insideexecute_queryat once, on TypeScript, Python and HTML.It surfaces as
Fatal Python error: Segmentation fault, not a Python exception, so nothing above it can catch or retry. The whole process dies mid-index.The lock is process-wide rather than per-language on purpose: the reports involve different languages crashing together, so a per-language lock would not have prevented them.
Cost
Negligible, because only the query step is serialised —
parser.parse(tree construction) and all Python-level work stay parallel:Within noise. Query execution is a small fraction of per-file work.
What I could not verify
I could not reproduce the segfault on this machine. 400 concurrent mixed-language parses across 16 threads survived five unlocked runs cleanly. The race is timing- and version-dependent — the reporter hit it on a mixed-language repo with a specific tree-sitter build.
So this is a guard against a reported native crash rather than one I reproduced, and the honest case for merging it is that the cost is ~1% and a segfault is unrecoverable and undebuggable for users. If you would rather wait for a local reproduction, that is a defensible call — but the trade looks strongly one-sided to me.
The tests therefore pin the guard, not the crash: a segfault cannot be asserted on from inside the process that takes it, and a passing concurrent run proves nothing about a nondeterministic race.
test_queries_never_overlap_under_concurrencyinstruments the query body and asserts the peak concurrent count is exactly 1 across 64 calls on 8 threads. 3 of the 4 tests fail without the fix.Knock-on
This unblocks #1052 (parallel incremental parse in the watcher), which I rebased but held precisely because it would have moved this crash from
cgc indexintocgc watch— where it is worse, since watch runs unattended for hours.🤖 Generated with Claude Code