fix(analyze): per-chunk C/C++ guard — workers for all other languages unchanged

Agent-Logs-Url: https://github.com/abhigyanpatwari/GitNexus/sessions/5591f1ba-9032-4706-b007-effb0df46eb8

Co-authored-by: magyargergo <11230420+magyargergo@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot] 2026-05-18 06:44:14 +00:00 committed by GitHub
parent f1593016da
commit 950f41bfdf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 39 additions and 10 deletions

View file

@ -221,19 +221,11 @@ export async function runChunkedParseAndResolve(
const MIN_FILES_FOR_WORKERS = options?.workerThresholdsForTest?.minFiles ?? 15;
const MIN_BYTES_FOR_WORKERS = options?.workerThresholdsForTest?.minBytes ?? 512 * 1024;
const totalBytes = parseableScanned.reduce((s, f) => s + f.size, 0);
const disableWorkersForNativeLanguages =
process.env.GITNEXUS_ALLOW_CPP_WORKERS !== '1' && hasWorkerUnsafeLanguages(parseableScanned);
if (disableWorkersForNativeLanguages) {
logger.warn(
'C/C++ files detected — using sequential parser path to avoid known worker-thread native binding errors (e.g. `Napi::Error`). If you use a custom tree-sitter build that resolves this issue, set GITNEXUS_ALLOW_CPP_WORKERS=1 to force workers.',
);
}
// Create worker pool once, reuse across chunks
let workerPool: WorkerPool | undefined;
if (
!options?.skipWorkers &&
!disableWorkersForNativeLanguages &&
(totalParseable >= MIN_FILES_FOR_WORKERS || totalBytes >= MIN_BYTES_FOR_WORKERS)
) {
try {
@ -324,6 +316,8 @@ export async function runChunkedParseAndResolve(
const parseCache = options?.parseCache;
let chunkCacheHits = 0;
let chunkCacheMisses = 0;
// Suppress duplicate warnings when multiple consecutive chunks contain C/C++ files.
let hasWarnedAboutCppChunks = false;
try {
for (let chunkIdx = 0; chunkIdx < numChunks; chunkIdx++) {
@ -379,6 +373,25 @@ export async function runChunkedParseAndResolve(
// them under the chunk hash for the next run.
chunkCacheMisses++;
const rawResults: ParseWorkerResult[] = [];
// C/C++ grammar native bindings can crash worker threads with an
// unrecoverable Napi::Error / std::terminate(). Detect C/C++ files
// within this specific chunk and route them through the main-thread
// sequential path to avoid the crash. Other chunks (no C/C++) keep
// using the worker pool. GITNEXUS_ALLOW_CPP_WORKERS=1 opts out of
// this guard when a stable custom build is in use.
const chunkHasCppFiles =
workerPool &&
process.env.GITNEXUS_ALLOW_CPP_WORKERS !== '1' &&
hasWorkerUnsafeLanguages(chunkFiles);
if (chunkHasCppFiles && !hasWarnedAboutCppChunks) {
hasWarnedAboutCppChunks = true;
logger.warn(
'C/C++ files detected — parsing those chunks in sequential mode to avoid known ' +
'worker-thread native binding errors (e.g. `Napi::Error`). C/C++ source files ' +
'are still fully indexed; chunks with no C/C++ continue using workers. ' +
'Set GITNEXUS_ALLOW_CPP_WORKERS=1 if you use a custom tree-sitter build without this issue.',
);
}
chunkWorkerData = await processParsing(
graph,
chunkFiles,
@ -400,7 +413,11 @@ export async function runChunkedParseAndResolve(
},
});
},
workerPool,
// For chunks containing C/C++ files, bypass the worker pool to avoid
// unrecoverable Napi::Error crashes from native grammar bindings in
// worker threads. processParsing falls through to processParsingSequential
// (main-thread safe). All other chunks keep using the worker pool.
chunkHasCppFiles ? undefined : workerPool,
// Capture raw results only when we have a cache to write to —
// otherwise we'd retain extra arrays for nothing.
parseCache && chunkHash ? rawResults : undefined,

View file

@ -12,7 +12,7 @@ describe('hasWorkerUnsafeLanguages', () => {
).toBe(false);
});
it('returns true when C/C++ files are present', () => {
it('returns true when C++ files are present', () => {
expect(
hasWorkerUnsafeLanguages([
{ path: 'src/main.ts', size: 123 },
@ -20,4 +20,16 @@ describe('hasWorkerUnsafeLanguages', () => {
]),
).toBe(true);
});
it('returns true when C files (.c extension) are present', () => {
expect(hasWorkerUnsafeLanguages([{ path: 'lib/util.c', size: 100 }])).toBe(true);
});
it('returns true when C header files are present', () => {
expect(hasWorkerUnsafeLanguages([{ path: 'include/api.h', size: 100 }])).toBe(true);
});
it('returns false for an empty list', () => {
expect(hasWorkerUnsafeLanguages([])).toBe(false);
});
});