mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 10:58:56 +00:00
Use low alignment bits instead of truncating high pointer bits
Changed PackedNext implementation to preserve ARM TBI/PAC/MTE pointer metadata:
- Store full usize pointer with auto_delete flag in LOW alignment bit (not high bit)
- ConcurrentTask is 8-byte aligned (contains u64 Task), so low bit is guaranteed zero
- Use enum(usize) { zero = 0, _ } for type safety and clean initialization syntax
- All atomic operations use usize (instead of u64) with @intFromEnum/@enumFromInt
- Masks: AUTO_DELETE_MASK=0x1, POINTER_MASK=~0x1
Benefits:
- Preserves all high pointer metadata (ARM pointer authentication, tagging, etc.)
- Uses guaranteed-zero low alignment bits instead of truncating address space
- Type-safe enum wrapper prevents accidental misuse
- Clean .zero initialization syntax
This commit is contained in:
@@ -11,57 +11,71 @@
|
||||
const ConcurrentTask = @This();
|
||||
|
||||
task: Task = undefined,
|
||||
next: PackedNext = .{},
|
||||
next: PackedNext = .zero,
|
||||
|
||||
pub const PackedNext = packed struct(u64) {
|
||||
ptr_bits: u63 = 0,
|
||||
auto_delete: bool = false,
|
||||
pub const PackedNext = enum(usize) {
|
||||
// Store the full usize pointer with auto_delete flag in the low alignment bit
|
||||
// ConcurrentTask contains a u64 Task field, so it's at least 8-byte aligned
|
||||
// This preserves all pointer metadata (ARM TBI/PAC/MTE tags, etc.)
|
||||
zero = 0,
|
||||
_,
|
||||
|
||||
const AUTO_DELETE_MASK: usize = 0x1;
|
||||
const POINTER_MASK: usize = ~AUTO_DELETE_MASK;
|
||||
|
||||
pub fn init(ptr: ?*ConcurrentTask) PackedNext {
|
||||
if (ptr) |p| {
|
||||
const addr = @intFromPtr(p);
|
||||
return .{
|
||||
.ptr_bits = @as(u63, @truncate(addr)),
|
||||
.auto_delete = false,
|
||||
};
|
||||
// Pointer should be aligned, verify low bit is zero
|
||||
if (bun.Environment.allow_assert) {
|
||||
bun.assertf((addr & AUTO_DELETE_MASK) == 0, "ConcurrentTask pointer must be aligned", .{});
|
||||
}
|
||||
// Store pointer with auto_delete = false (low bit = 0)
|
||||
return @enumFromInt(addr);
|
||||
}
|
||||
return .{};
|
||||
return @enumFromInt(0);
|
||||
}
|
||||
|
||||
pub fn initPreserveAutoDelete(self: PackedNext, ptr: ?*ConcurrentTask) PackedNext {
|
||||
const self_val = @intFromEnum(self);
|
||||
if (ptr) |p| {
|
||||
const addr = @intFromPtr(p);
|
||||
return .{
|
||||
.ptr_bits = @as(u63, @truncate(addr)),
|
||||
.auto_delete = self.auto_delete,
|
||||
};
|
||||
// Pointer should be aligned, verify low bit is zero
|
||||
if (bun.Environment.allow_assert) {
|
||||
bun.assertf((addr & AUTO_DELETE_MASK) == 0, "ConcurrentTask pointer must be aligned", .{});
|
||||
}
|
||||
// Combine new pointer with existing auto_delete flag
|
||||
return @enumFromInt(addr | (self_val & AUTO_DELETE_MASK));
|
||||
}
|
||||
return .{
|
||||
.ptr_bits = 0,
|
||||
.auto_delete = self.auto_delete,
|
||||
};
|
||||
// Null pointer but preserve auto_delete flag
|
||||
return @enumFromInt(self_val & AUTO_DELETE_MASK);
|
||||
}
|
||||
|
||||
pub fn get(self: PackedNext) ?*ConcurrentTask {
|
||||
if (self.ptr_bits == 0) return null;
|
||||
// Explicitly zero out bit 63 to avoid any UB
|
||||
const ptr: u64 = @as(u64, self.ptr_bits) & 0x7FFFFFFFFFFFFFFF;
|
||||
return @as(?*ConcurrentTask, @ptrFromInt(ptr));
|
||||
// Mask out the auto_delete bit to get the original pointer
|
||||
const addr = @intFromEnum(self) & POINTER_MASK;
|
||||
if (addr == 0) return null;
|
||||
return @ptrFromInt(addr);
|
||||
}
|
||||
|
||||
pub fn autoDelete(self: PackedNext) bool {
|
||||
return self.auto_delete;
|
||||
return (@intFromEnum(self) & AUTO_DELETE_MASK) != 0;
|
||||
}
|
||||
|
||||
pub fn setAutoDelete(self: *PackedNext, value: bool) void {
|
||||
// Non-atomic write is safe because this is only called during initialization
|
||||
// before the task is shared with other threads
|
||||
self.auto_delete = value;
|
||||
const self_val = @intFromEnum(self.*);
|
||||
if (value) {
|
||||
self.* = @enumFromInt(self_val | AUTO_DELETE_MASK);
|
||||
} else {
|
||||
self.* = @enumFromInt(self_val & POINTER_MASK);
|
||||
}
|
||||
}
|
||||
|
||||
comptime {
|
||||
if (@sizeOf(PackedNext) != @sizeOf(u64)) {
|
||||
@compileError("PackedNext must be the same size as a u64");
|
||||
if (@sizeOf(PackedNext) != @sizeOf(usize)) {
|
||||
@compileError("PackedNext must be the same size as a usize");
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -83,7 +97,7 @@ pub const AutoDeinit = enum {
|
||||
pub fn create(task: Task) *ConcurrentTask {
|
||||
var concurrent_task = ConcurrentTask.new(.{
|
||||
.task = task,
|
||||
.next = .{},
|
||||
.next = .zero,
|
||||
});
|
||||
concurrent_task.next.setAutoDelete(true);
|
||||
return concurrent_task;
|
||||
@@ -105,7 +119,7 @@ pub fn from(this: *ConcurrentTask, of: anytype, auto_deinit: AutoDeinit) *Concur
|
||||
|
||||
this.* = .{
|
||||
.task = Task.init(of),
|
||||
.next = .{},
|
||||
.next = .zero,
|
||||
};
|
||||
this.next.setAutoDelete(auto_deinit == .auto_deinit);
|
||||
return this;
|
||||
|
||||
@@ -27,7 +27,7 @@ export fn Bun__queueJSCDeferredWorkTaskConcurrently(jsc_vm: *VirtualMachine, tas
|
||||
var loop = jsc_vm.eventLoop();
|
||||
const concurrent_task = ConcurrentTask.new(.{
|
||||
.task = Task.init(task),
|
||||
.next = .{},
|
||||
.next = .zero,
|
||||
});
|
||||
concurrent_task.next.setAutoDelete(true);
|
||||
loop.enqueueTaskConcurrent(concurrent_task);
|
||||
|
||||
Reference in New Issue
Block a user