Compare commits

...

1 Commits

Author SHA1 Message Date
Claude Bot
65b8b68824 fix(install): prevent race condition in logger from package manager tasks
Fixes crash caused by concurrent writes to errorWriter when package manager
tasks complete on worker threads. Previously, task.log.print() was called
directly from runTasks, causing race conditions when multiple tasks completed
simultaneously.

Solution:
- Collect task logs into manager.log instead of printing directly
- Print accumulated logs at end of runTasks function on main thread
- This ensures all logging happens on a single thread

The crash manifested as a segmentation fault in simdutf formatting code
when multiple threads tried to write to the same output stream.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-20 07:22:35 +00:00
2 changed files with 63 additions and 2 deletions

View File

@@ -482,11 +482,12 @@ pub fn runTasks(
manager.decrementPendingTasks();
if (task.log.msgs.items.len > 0) {
try task.log.print(Output.errorWriter());
// Append task log to manager log instead of printing directly
// This avoids race conditions when multiple tasks complete simultaneously
try task.log.appendTo(manager.log);
if (task.log.errors > 0) {
manager.any_failed_to_install = true;
}
task.log.deinit();
}
switch (task.tag) {
@@ -858,6 +859,12 @@ pub fn runTasks(
},
}
}
// Print any accumulated logs from tasks to avoid race conditions
if (manager.log.msgs.items.len > 0) {
manager.log.print(Output.errorWriter()) catch {};
manager.log.reset();
}
}
pub inline fn pendingTaskCount(manager: *const PackageManager) u32 {

View File

@@ -0,0 +1,54 @@
import { test, expect } from "bun:test";
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
import { join } from "path";
test("package manager logger race condition fix", async () => {
// This test verifies that the logger race condition is fixed
// by attempting to trigger simultaneous package installation tasks
// that would previously cause a segmentation fault
const dir = tempDirWithFiles("logger-race-test", {
"package.json": JSON.stringify({
name: "test-race-condition",
version: "1.0.0",
dependencies: {
// Multiple small packages to trigger concurrent tasks
"is-array": "1.0.1",
"is-string": "1.0.4",
"is-number": "7.0.0",
"is-boolean": "1.0.1",
"is-function": "1.0.1",
"is-object": "1.0.1",
"is-regexp": "1.0.0",
"is-date": "1.0.1"
}
})
});
// Run install multiple times to increase chance of hitting race condition
for (let i = 0; i < 3; i++) {
await using proc = Bun.spawn({
cmd: [bunExe(), "install"],
env: bunEnv,
cwd: dir,
stdio: ["ignore", "pipe", "pipe"]
});
const [stdout, stderr, exitCode] = await Promise.all([
new Response(proc.stdout).text(),
new Response(proc.stderr).text(),
proc.exited
]);
// The main assertion is that bun doesn't crash with a segfault
// If the race condition exists, this would likely fail
expect(exitCode).toBe(0);
// Clean up node_modules for next iteration
await using rmProc = Bun.spawn({
cmd: ["rm", "-rf", "node_modules"],
cwd: dir
});
await rmProc.exited;
}
}, { timeout: 30000 });