Compare commits

...

1 Commits

Author SHA1 Message Date
Jarred Sumner
23ab2bb94f Add assertions for calling ref/unref on the proper thread 2025-06-06 16:25:43 -07:00
7 changed files with 92 additions and 2 deletions

View File

@@ -55,10 +55,18 @@ pub const KeepAlive = struct {
this.status = .inactive;
if (comptime @TypeOf(event_loop_ctx_) == JSC.EventLoopHandle) {
if (comptime bun.Environment.debug_checks) {
event_loop_ctx_.assertEventLoopThread();
}
event_loop_ctx_.loop().unref();
return;
}
const event_loop_ctx = JSC.AbstractVM(event_loop_ctx_);
if (comptime Environment.debug_checks) {
event_loop_ctx.assertEventLoopThread();
}
event_loop_ctx.platformEventLoop().unref();
}
@@ -96,10 +104,18 @@ pub const KeepAlive = struct {
this.status = .active;
const EventLoopContext = @TypeOf(event_loop_ctx_);
if (comptime EventLoopContext == JSC.EventLoopHandle) {
if (comptime bun.Environment.debug_checks) {
event_loop_ctx_.assertEventLoopThread();
}
event_loop_ctx_.ref();
return;
}
const event_loop_ctx = JSC.AbstractVM(event_loop_ctx_);
if (comptime Environment.debug_checks) {
event_loop_ctx.assertEventLoopThread();
}
event_loop_ctx.platformEventLoop().ref();
}

View File

@@ -55,10 +55,18 @@ pub const KeepAlive = struct {
return;
this.status = .inactive;
if (comptime @TypeOf(event_loop_ctx_) == JSC.EventLoopHandle) {
if (comptime bun.Environment.debug_checks) {
event_loop_ctx_.assertEventLoopThread();
}
event_loop_ctx_.loop().subActive(1);
return;
}
const event_loop_ctx = JSC.AbstractVM(event_loop_ctx_);
if (comptime bun.Environment.debug_checks) {
event_loop_ctx.assertEventLoopThread();
}
event_loop_ctx.platformEventLoop().subActive(1);
}
@@ -76,6 +84,10 @@ pub const KeepAlive = struct {
if (this.status != .active)
return;
this.status = .inactive;
if (comptime bun.Environment.debug_checks) {
JSC.AbstractVM(vm).assertEventLoopThread();
}
vm.event_loop_handle.?.dec();
}
@@ -95,10 +107,18 @@ pub const KeepAlive = struct {
this.status = .active;
const EventLoopContext = @TypeOf(event_loop_ctx_);
if (comptime EventLoopContext == JSC.EventLoopHandle) {
if (comptime bun.Environment.debug_checks) {
event_loop_ctx_.assertEventLoopThread();
}
event_loop_ctx_.ref();
return;
}
const event_loop_ctx = JSC.AbstractVM(event_loop_ctx_);
if (comptime bun.Environment.debug_checks) {
event_loop_ctx.assertEventLoopThread();
}
event_loop_ctx.platformEventLoop().ref();
}

View File

@@ -221,7 +221,11 @@ pub fn uvLoop(this: *const VirtualMachine) *bun.Async.Loop {
return this.event_loop_handle.?;
}
pub fn isMainThread(this: *const VirtualMachine) bool {
pub fn isJavaScriptThread(this: *const VirtualMachine) bool {
return this == VMHolder.vm;
}
pub fn isMainThreadVM(this: *const VirtualMachine) bool {
return this.worker == null;
}
@@ -3492,7 +3496,7 @@ pub const ExitHandler = struct {
JSC.markBinding(@src());
const vm: *VirtualMachine = @alignCast(@fieldParentPtr("exit_handler", this));
Process__dispatchOnExit(vm.global, this.exit_code);
if (vm.isMainThread()) {
if (vm.isMainThreadVM()) {
Bun__closeAllSQLiteDatabasesForTermination();
}
}

View File

@@ -606,6 +606,10 @@ pub fn unrefConcurrently(this: *EventLoop) void {
this.wakeup();
}
pub fn isEventLoopThread(this: *const EventLoop) bool {
return this.virtual_machine.isJavaScriptThread();
}
pub const AnyEventLoop = @import("./event_loop/AnyEventLoop.zig").AnyEventLoop;
pub const ConcurrentPromiseTask = @import("./event_loop/ConcurrentPromiseTask.zig").ConcurrentPromiseTask;
pub const WorkTask = @import("./event_loop/WorkTask.zig").WorkTask;

View File

@@ -90,6 +90,13 @@ pub const EventLoopHandle = union(EventLoopKind) {
}
}
pub fn assertEventLoopThread(this: EventLoopHandle) void {
switch (this) {
.js => JSC.AbstractVM(this.js.virtual_machine).assertEventLoopThread(),
.mini => JSC.AbstractVM(this.mini).assertEventLoopThread(),
}
}
pub fn loop(this: EventLoopHandle) *bun.uws.Loop {
return switch (this) {
.js => this.js.usocketsLoop(),
@@ -107,10 +114,18 @@ pub const EventLoopHandle = union(EventLoopKind) {
pub const platformEventLoop = loop;
pub fn ref(this: EventLoopHandle) void {
if (comptime bun.Environment.debug_checks) {
this.assertEventLoopThread();
}
this.loop().ref();
}
pub fn unref(this: EventLoopHandle) void {
if (comptime bun.Environment.debug_checks) {
this.assertEventLoopThread();
}
this.loop().unref();
}

View File

@@ -39,6 +39,10 @@ pub threadlocal var global: *MiniEventLoop = undefined;
pub const ConcurrentTaskQueue = UnboundedQueue(AnyTaskWithExtraContext, .next);
pub fn isEventLoopThread(this: *const MiniEventLoop) bool {
return this == global;
}
pub fn initGlobal(env: ?*bun.DotEnv.Loader) *MiniEventLoop {
if (globalInitialized) return global;
const loop = MiniEventLoop.init(bun.default_allocator);
@@ -282,6 +286,20 @@ pub fn stdout(this: *MiniEventLoop) *JSC.WebCore.Blob.Store {
};
}
const shared = struct {
pub fn assertEventLoopThread(this: anytype) void {
if (!this.isEventLoopThread()) {
var buf: [128]u8 = undefined;
const rc: c_int = if (Environment.isPosix) std.c.pthread_getname_np(std.c.pthread_self(), @ptrCast(&buf), buf.len) else 0;
if (rc == 0) {
bun.Output.panic("ref or unref called from non-event-loop thread: {s}. Call it from the event loop thread.", .{bun.sliceTo(&buf, 0)});
} else {
@panic("ref or unref called from non-event-loop thread. Call it from the event loop thread.");
}
}
}
};
pub const JsVM = struct {
vm: *VirtualMachine,
@@ -291,10 +309,16 @@ pub const JsVM = struct {
};
}
pub inline fn isEventLoopThread(this: @This()) bool {
return this.vm.isJavaScriptThread();
}
pub inline fn loop(this: @This()) *JSC.EventLoop {
return this.vm.event_loop;
}
pub const assertEventLoopThread = shared.assertEventLoopThread;
pub inline fn allocFilePoll(this: @This()) *bun.Async.FilePoll {
return this.vm.rareData().filePolls(this.vm).get();
}
@@ -329,6 +353,12 @@ pub const MiniVM = struct {
return this.mini.filePolls().get();
}
pub inline fn isEventLoopThread(this: @This()) bool {
return this.mini.isEventLoopThread();
}
pub const assertEventLoopThread = shared.assertEventLoopThread;
pub inline fn platformEventLoop(this: @This()) *JSC.PlatformEventLoop {
if (comptime Environment.isWindows) {
return this.mini.loop.uv_loop;

View File

@@ -26,6 +26,7 @@ pub const isX86 = @import("builtin").target.cpu.arch.isX86();
pub const isX64 = @import("builtin").target.cpu.arch == .x86_64;
pub const isMusl = builtin.target.abi.isMusl();
pub const allow_assert = isDebug or isTest or std.builtin.Mode.ReleaseSafe == @import("builtin").mode;
pub const debug_checks = isDebug or enable_asan;
pub const show_crash_trace = isDebug or isTest or enable_asan;
/// All calls to `@export` should be gated behind this check, so that code
/// generators that compile Zig code know not to reference and compile a ton of