Skip to content

fix(parsers): serialise tree-sitter query execution across threads - #1593

Merged
Shashankss1205 merged 1 commit into
mainfrom
fix/tree-sitter-query-concurrency
Aug 8, 2026
Merged

Shashankss1205 merged 1 commit into
mainfrom
fix/tree-sitter-query-concurrency

Conversation

@Shashankss1205

Copy link
Copy Markdown
Collaborator

Fixes #1370. Full suite: 1157 passed, 7 skipped.

The bug

tree_sitter_manager locked its language cache (_cache_lock) but nothing guarded query execution:

def execute_query(language, query_string, node):
    query = Query(language, query_string)   # per call, no lock
    cursor = QueryCursor(query)
    res = cursor.captures(node)

cgc index runs parsers on a thread pool, so several threads reach this concurrently on different grammars. #1370's traceback shows exactly that — three threads inside execute_query at 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:

120 parses, 8 threads, mixed Python/TypeScript
  with lock:    3.48s  median of 3
  without:      3.45s  median of 3

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_concurrency instruments 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 index into cgc watch — where it is worse, since watch runs unattended for hours.

🤖 Generated with Claude Code

…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>
@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Hi! 👋 Join our CodeGraphContext Discord channel to collaborate: https://discord.gg/dR4QY32uYQ

@vercel

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
code-graph-context-pack Ready Ready Preview Aug 8, 2026 9:11pm

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

🔍 PR Code Graph Analysis

fix(parsers): serialise tree-sitter query execution across threads (#1593)

📊 Interactive Visualization

View the blast radius graph: PR Reviewer Dashboard

📦 Artifacts

The graph JSON has been uploaded as a build artifact: pr-code-graph-1593


Generated by CodeGraphContext using FalkorDB Lite

@Shashankss1205
Shashankss1205 merged commit c85eb54 into main Aug 8, 2026
20 checks passed
@Shashankss1205
Shashankss1205 deleted the fix/tree-sitter-query-concurrency branch August 8, 2026 21:18
@github-project-automation github-project-automation Bot moved this from Backlog tasks to Done in CGC Progress Board Aug 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Bug: Segfault during 'cgc index' — concurrent tree-sitter execute_query() calls across worker threads (race condition in native extension)

1 participant