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,
|
||||
.scan_pass_result = ScanPassResult.init(bun.default_allocator),
|
||||
});
|
||||
errdefer bun.destroy(this);
|
||||
errdefer this.arena.deinit();
|
||||
errdefer {
|
||||
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 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.
|
||||
pub fn assertNoRefs(count: *const @This()) void {
|
||||
if (enable_debug) {
|
||||
if (comptime bun.Environment.ci_assert) {
|
||||
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 {
|
||||
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.
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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() {
|
||||
return &@field(self, field_name);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user