mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Fix assertion failure in JSTranspiler (#22409)
* Fix assertion failure when calling `bun.destroy` on a partially-initialized `JSTranspiler`. * Add a new method, `RefCount.clearWithoutDestructor`, to make this pattern possible. * Enable ref count assertion in `bun.destroy` for CI builds, not just debug. (For internal tracking: fixes STAB-1123, STAB-1124)
This commit is contained in:
@@ -649,8 +649,12 @@ pub fn constructor(globalThis: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) b
|
|||||||
.transpiler = undefined,
|
.transpiler = undefined,
|
||||||
.scan_pass_result = ScanPassResult.init(bun.default_allocator),
|
.scan_pass_result = ScanPassResult.init(bun.default_allocator),
|
||||||
});
|
});
|
||||||
errdefer bun.destroy(this);
|
errdefer {
|
||||||
errdefer this.arena.deinit();
|
this.config.log.deinit();
|
||||||
|
this.arena.deinit();
|
||||||
|
this.ref_count.clearWithoutDestructor();
|
||||||
|
bun.destroy(this);
|
||||||
|
}
|
||||||
|
|
||||||
const config_arg = if (arguments.len > 0) arguments.ptr[0] else .js_undefined;
|
const config_arg = if (arguments.len > 0) arguments.ptr[0] else .js_undefined;
|
||||||
const allocator = this.arena.allocator();
|
const allocator = this.arena.allocator();
|
||||||
|
|||||||
@@ -175,11 +175,21 @@ pub fn RefCount(T: type, field_name: []const u8, destructor: anytype, options: O
|
|||||||
|
|
||||||
/// The count is 0 after the destructor is called.
|
/// The count is 0 after the destructor is called.
|
||||||
pub fn assertNoRefs(count: *const @This()) void {
|
pub fn assertNoRefs(count: *const @This()) void {
|
||||||
if (enable_debug) {
|
if (comptime bun.Environment.ci_assert) {
|
||||||
bun.assert(count.raw_count == 0);
|
bun.assert(count.raw_count == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the ref count to 0 without running the destructor.
|
||||||
|
///
|
||||||
|
/// Only use this if you're about to free the object (e.g., with `bun.destroy`).
|
||||||
|
///
|
||||||
|
/// Don't modify the ref count or create any `RefPtr`s after calling this method.
|
||||||
|
pub fn clearWithoutDestructor(count: *@This()) void {
|
||||||
|
count.assertSingleThreaded();
|
||||||
|
count.raw_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
fn assertSingleThreaded(count: *@This()) void {
|
fn assertSingleThreaded(count: *@This()) void {
|
||||||
count.thread.lockOrAssert();
|
count.thread.lockOrAssert();
|
||||||
}
|
}
|
||||||
@@ -282,11 +292,23 @@ pub fn ThreadSafeRefCount(T: type, field_name: []const u8, destructor: fn (*T) v
|
|||||||
|
|
||||||
/// The count is 0 after the destructor is called.
|
/// The count is 0 after the destructor is called.
|
||||||
pub fn assertNoRefs(count: *const @This()) void {
|
pub fn assertNoRefs(count: *const @This()) void {
|
||||||
if (enable_debug) {
|
if (comptime bun.Environment.ci_assert) {
|
||||||
bun.assert(count.raw_count.load(.seq_cst) == 0);
|
bun.assert(count.raw_count.load(.seq_cst) == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Sets the ref count to 0 without running the destructor.
|
||||||
|
///
|
||||||
|
/// Only use this if you're about to free the object (e.g., with `bun.destroy`).
|
||||||
|
///
|
||||||
|
/// Don't modify the ref count or create any `RefPtr`s after calling this method.
|
||||||
|
pub fn clearWithoutDestructor(count: *@This()) void {
|
||||||
|
// This method should only be used if you're about the free the object. You shouldn't
|
||||||
|
// be freeing the object if other threads might be using it, and no memory order can
|
||||||
|
// help with that, so .monotonic is sufficient.
|
||||||
|
count.raw_count.store(0, .monotonic);
|
||||||
|
}
|
||||||
|
|
||||||
fn getRefCount(self: *T) *@This() {
|
fn getRefCount(self: *T) *@This() {
|
||||||
return &@field(self, field_name);
|
return &@field(self, field_name);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user