mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
fix: Hang when recursively logging via ConsoleObject.messageWithTypeAndLevel (#8213)
* Avoid deadlock in messageWithTypeAndLevel by adding a recursion counter * Add test for recursive logs * Do not rely on output of console-recursive.test.js * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
@@ -72,6 +72,9 @@ pub const MessageType = enum(u32) {
|
||||
var stderr_mutex: bun.Lock = bun.Lock.init();
|
||||
var stdout_mutex: bun.Lock = bun.Lock.init();
|
||||
|
||||
threadlocal var stderr_lock_count: u16 = 0;
|
||||
threadlocal var stdout_lock_count: u16 = 0;
|
||||
|
||||
/// https://console.spec.whatwg.org/#formatter
|
||||
pub fn messageWithTypeAndLevel(
|
||||
//console_: ConsoleObject.Type,
|
||||
@@ -92,15 +95,32 @@ pub fn messageWithTypeAndLevel(
|
||||
// Lock/unlock a mutex incase two JS threads are console.log'ing at the same time
|
||||
// We do this the slightly annoying way to avoid assigning a pointer
|
||||
if (level == .Warning or level == .Error or message_type == .Assert) {
|
||||
stderr_mutex.lock();
|
||||
if (stderr_lock_count == 0) {
|
||||
stderr_mutex.lock();
|
||||
}
|
||||
|
||||
stderr_lock_count += 1;
|
||||
} else {
|
||||
stdout_mutex.lock();
|
||||
if (stdout_lock_count == 0) {
|
||||
stdout_mutex.lock();
|
||||
}
|
||||
|
||||
stdout_lock_count += 1;
|
||||
}
|
||||
|
||||
defer {
|
||||
if (level == .Warning or level == .Error or message_type == .Assert) {
|
||||
stderr_mutex.unlock();
|
||||
stderr_lock_count -= 1;
|
||||
|
||||
if (stderr_lock_count == 0) {
|
||||
stderr_mutex.unlock();
|
||||
}
|
||||
} else {
|
||||
stdout_mutex.unlock();
|
||||
stdout_lock_count -= 1;
|
||||
|
||||
if (stdout_lock_count == 0) {
|
||||
stdout_mutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
16
test/js/web/console/console-recursive.js
Normal file
16
test/js/web/console/console-recursive.js
Normal file
@@ -0,0 +1,16 @@
|
||||
// We use a relatively random string for the stderr flag.
|
||||
// Just to avoid matching against something in the executable path.
|
||||
const use_err = process.argv.includes("print_to_stderr_skmxctoznf");
|
||||
|
||||
const log_method = use_err ? console.error : console.log;
|
||||
|
||||
class MyMap extends Map {
|
||||
get size() {
|
||||
log_method("inside size");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const map = new MyMap();
|
||||
|
||||
log_method("map:", map);
|
||||
26
test/js/web/console/console-recursive.test.ts
Normal file
26
test/js/web/console/console-recursive.test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
// @known-failing-on-windows: 1 failing
|
||||
import { spawn } from "bun";
|
||||
import { expect, it } from "bun:test";
|
||||
import { bunExe, bunEnv } from "harness";
|
||||
|
||||
it("should not hang when logging to stdout recursively", async () => {
|
||||
const { exited } = spawn({
|
||||
cmd: [bunExe(), import.meta.dir + "/console-recursive.js"],
|
||||
stdin: null,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env: bunEnv,
|
||||
});
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
|
||||
it("should not hang when logging to stderr recursively", async () => {
|
||||
const { exited } = spawn({
|
||||
cmd: [bunExe(), import.meta.dir + "/console-recursive.js", "print_to_stderr_skmxctoznf"],
|
||||
stdin: null,
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env: bunEnv,
|
||||
});
|
||||
expect(await exited).toBe(0);
|
||||
});
|
||||
Reference in New Issue
Block a user