Skip to content
Closed

WIP #23151

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions src/allocators.zig
Original file line number Diff line number Diff line change
Expand Up @@ -928,6 +928,127 @@ const basic = if (bun.use_mimalloc)
else
@import("./allocators/fallback.zig");

pub fn stackFallback(comptime size: usize, fallback_allocator: std.mem.Allocator) StackFallbackAllocator(size) {
return StackFallbackAllocator(size){
.buffer = undefined,
.fallback_allocator = fallback_allocator,
.fixed_buffer_allocator = undefined,
.force_heap = if (comptime Environment.ci_assert)
!bun.getRuntimeFeatureFlag(.BUN_DEBUG_FORCE_HEAP_FALLBACK_ALLOCATORS)
else {},
};
Comment on lines +936 to +939

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

force_heap flag is inverted (breaks CI “force heap” behavior).

The PR description says the runtime feature flag forces heap fallback in CI. Current code negates it, so enabling BUN_DEBUG_FORCE_HEAP_FALLBACK_ALLOCATORS disables forcing.

Apply this diff:

-        .force_heap = if (comptime Environment.ci_assert)
-            !bun.getRuntimeFeatureFlag(.BUN_DEBUG_FORCE_HEAP_FALLBACK_ALLOCATORS)
-        else {},
+        .force_heap = if (comptime Environment.ci_assert)
+            bun.getRuntimeFeatureFlag(.BUN_DEBUG_FORCE_HEAP_FALLBACK_ALLOCATORS)
+        else {},
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.force_heap = if (comptime Environment.ci_assert)
!bun.getRuntimeFeatureFlag(.BUN_DEBUG_FORCE_HEAP_FALLBACK_ALLOCATORS)
else {},
};
.force_heap = if (comptime Environment.ci_assert)
bun.getRuntimeFeatureFlag(.BUN_DEBUG_FORCE_HEAP_FALLBACK_ALLOCATORS)
else {},
};
🤖 Prompt for AI Agents
In src/allocators.zig around lines 936 to 939, the .force_heap assignment
inverts the BUN_DEBUG_FORCE_HEAP_FALLBACK_ALLOCATORS flag (using !) so enabling
the flag disables the CI "force heap" behavior; remove the negation so
.force_heap uses
bun.getRuntimeFeatureFlag(.BUN_DEBUG_FORCE_HEAP_FALLBACK_ALLOCATORS) directly
when Environment.ci_assert is true, leaving the else branch unchanged.

}

/// An allocator that attempts to allocate using a
/// `FixedBufferAllocator` using an array of size `size`. If the
/// allocation fails, it will fall back to using
/// `fallback_allocator`. Easily created with `stackFallback`.
pub fn StackFallbackAllocator(comptime size: usize) type {
return struct {
const Self = @This();

buffer: [size]u8,
fallback_allocator: std.mem.Allocator,
fixed_buffer_allocator: std.heap.FixedBufferAllocator,
get_called: if (Environment.ci_assert) bool else void = if (Environment.ci_assert) false,
force_heap: if (Environment.ci_assert) bool else void,

/// This function both fetches a `Allocator` interface to this
/// allocator *and* resets the internal buffer allocator.
pub fn get(self: *Self) std.mem.Allocator {
if (comptime Environment.ci_assert) {
bun.assert(!self.get_called); // `get` called multiple times; instead use `const allocator = stackFallback(N).get();`
self.get_called = true;
}
self.fixed_buffer_allocator = std.heap.FixedBufferAllocator.init(self.buffer[0..]);
return .{
.ptr = self,
.vtable = &.{
.alloc = alloc,
.resize = resize,
.remap = remap,
.free = free,
},
};
}

fn alloc(
ctx: *anyopaque,
len: usize,
alignment: std.mem.Alignment,
ra: usize,
) ?[*]u8 {
const self: *Self = @ptrCast(@alignCast(ctx));
if (comptime Environment.ci_assert) {
if (self.force_heap) {
return self.fallback_allocator.rawAlloc(len, alignment, ra);
}
}
return std.heap.FixedBufferAllocator.alloc(&self.fixed_buffer_allocator, len, alignment, ra) orelse
return self.fallback_allocator.rawAlloc(len, alignment, ra);
}

fn resize(
ctx: *anyopaque,
buf: []u8,
alignment: std.mem.Alignment,
new_len: usize,
ra: usize,
) bool {
const self: *Self = @ptrCast(@alignCast(ctx));
if (comptime Environment.ci_assert) {
if (self.force_heap) {
return self.fallback_allocator.rawResize(buf, alignment, new_len, ra);
}
}
if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
return std.heap.FixedBufferAllocator.resize(&self.fixed_buffer_allocator, buf, alignment, new_len, ra);
} else {
return self.fallback_allocator.rawResize(buf, alignment, new_len, ra);
}
}

fn remap(
context: *anyopaque,
memory: []u8,
alignment: std.mem.Alignment,
new_len: usize,
return_address: usize,
) ?[*]u8 {
const self: *Self = @ptrCast(@alignCast(context));
if (comptime Environment.ci_assert) {
if (self.force_heap) {
return self.fallback_allocator.rawRemap(memory, alignment, new_len, return_address);
}
}
if (self.fixed_buffer_allocator.ownsPtr(memory.ptr)) {
return std.heap.FixedBufferAllocator.remap(&self.fixed_buffer_allocator, memory, alignment, new_len, return_address);
} else {
return self.fallback_allocator.rawRemap(memory, alignment, new_len, return_address);
}
}

fn free(
ctx: *anyopaque,
buf: []u8,
alignment: std.mem.Alignment,
ra: usize,
) void {
const self: *Self = @ptrCast(@alignCast(ctx));
if (comptime Environment.ci_assert) {
if (self.force_heap) {
return self.fallback_allocator.rawFree(buf, alignment, ra);
}
}
if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) {
return std.heap.FixedBufferAllocator.free(&self.fixed_buffer_allocator, buf, alignment, ra);
} else {
return self.fallback_allocator.rawFree(buf, alignment, ra);
}
}
};
}

const Environment = @import("./env.zig");
const std = @import("std");

Expand Down
2 changes: 1 addition & 1 deletion src/ast/P.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3537,7 +3537,7 @@ pub fn NewParser_(
// Insert any relocated variable statements now
if (p.relocated_top_level_vars.items.len > 0) {
var already_declared = RefMap{};
var already_declared_allocator_stack = std.heap.stackFallback(1024, allocator);
var already_declared_allocator_stack = bun.allocators.stackFallback(1024, allocator);
const already_declared_allocator = already_declared_allocator_stack.get();
defer if (already_declared_allocator_stack.fixed_buffer_allocator.end_index >= 1023) already_declared.deinit(already_declared_allocator);

Expand Down
28 changes: 14 additions & 14 deletions src/bake/DevServer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,7 @@ fn ensureRouteIsBundled(
}

// Prepare a bundle with just this route.
var sfa = std.heap.stackFallback(4096, dev.allocator());
var sfa = bun.allocators.stackFallback(4096, dev.allocator());
const temp_alloc = sfa.get();

var entry_points: EntryPointList = .empty;
Expand Down Expand Up @@ -1242,7 +1242,7 @@ fn checkRouteFailures(
route_bundle_index: RouteBundle.Index,
resp: DevResponse,
) !enum { stop, ok, rebuild } {
var sfa_state = std.heap.stackFallback(65536, dev.allocator());
var sfa_state = bun.allocators.stackFallback(65536, dev.allocator());
const sfa = sfa_state.get();
var gts = try dev.initGraphTraceState(sfa, 0);
defer gts.deinit(sfa);
Expand Down Expand Up @@ -1535,7 +1535,7 @@ fn generateHTMLPayload(dev: *DevServer, route_bundle_index: RouteBundle.Index, r
defer dev.graph_safety_lock.unlock();

// Prepare bitsets for tracing
var sfa_state = std.heap.stackFallback(65536, dev.allocator());
var sfa_state = bun.allocators.stackFallback(65536, dev.allocator());
const sfa = sfa_state.get();
var gts = try dev.initGraphTraceState(sfa, 0);
defer gts.deinit(sfa);
Expand Down Expand Up @@ -1587,7 +1587,7 @@ fn generateJavaScriptCodeForHTMLFile(
input_file_sources: []bun.logger.Source,
loaders: []bun.options.Loader,
) bun.OOM![]const u8 {
var sfa_state = std.heap.stackFallback(65536, dev.allocator());
var sfa_state = bun.allocators.stackFallback(65536, dev.allocator());
const sfa = sfa_state.get();
var array = bun.handleOom(std.ArrayListUnmanaged(u8).initCapacity(sfa, 65536));
defer array.deinit(sfa);
Expand Down Expand Up @@ -1836,7 +1836,7 @@ pub fn startAsyncBundle(

// Notify inspector about bundle start
if (dev.inspector()) |agent| {
var sfa_state = std.heap.stackFallback(256, dev.allocator());
var sfa_state = bun.allocators.stackFallback(256, dev.allocator());
const sfa = sfa_state.get();
var trigger_files = try std.ArrayList(bun.String).initCapacity(sfa, entry_points.set.count());
defer trigger_files.deinit();
Expand Down Expand Up @@ -1931,7 +1931,7 @@ pub fn prepareAndLogResolutionFailures(dev: *DevServer) !void {

fn indexFailures(dev: *DevServer) !void {
// After inserting failures into the IncrementalGraphs, they are traced to their routes.
var sfa_state = std.heap.stackFallback(65536, dev.allocator());
var sfa_state = bun.allocators.stackFallback(65536, dev.allocator());
const sfa = sfa_state.get();

if (dev.incremental_result.failures_added.items.len > 0) {
Expand Down Expand Up @@ -2014,7 +2014,7 @@ fn generateClientBundle(dev: *DevServer, route_bundle: *RouteBundle) bun.OOM![]u
defer dev.graph_safety_lock.unlock();

// Prepare bitsets
var sfa_state = std.heap.stackFallback(65536, dev.allocator());
var sfa_state = bun.allocators.stackFallback(65536, dev.allocator());
const sfa = sfa_state.get();
var gts = try dev.initGraphTraceState(sfa, 0);
defer gts.deinit(sfa);
Expand Down Expand Up @@ -2078,7 +2078,7 @@ fn generateCssJSArray(dev: *DevServer, route_bundle: *RouteBundle) bun.JSError!j
defer dev.graph_safety_lock.unlock();

// Prepare bitsets
var sfa_state = std.heap.stackFallback(65536, dev.allocator());
var sfa_state = bun.allocators.stackFallback(65536, dev.allocator());

const sfa = sfa_state.get();
var gts = try dev.initGraphTraceState(sfa, 0);
Expand Down Expand Up @@ -2252,7 +2252,7 @@ pub fn finalizeBundle(
const targets = bv2.graph.ast.items(.target);
const scbs = bv2.graph.server_component_boundaries.slice();

var sfa = std.heap.stackFallback(65536, bv2.allocator());
var sfa = bun.allocators.stackFallback(65536, bv2.allocator());
const stack_alloc = sfa.get();
var scb_bitset = try bun.bit_set.DynamicBitSetUnmanaged.initEmpty(stack_alloc, input_file_sources.len);
for (
Expand Down Expand Up @@ -2569,7 +2569,7 @@ pub fn finalizeBundle(

var has_route_bits_set = false;

var hot_update_payload_sfa = std.heap.stackFallback(65536, dev.allocator());
var hot_update_payload_sfa = bun.allocators.stackFallback(65536, dev.allocator());
var hot_update_payload = std.ArrayList(u8).initCapacity(hot_update_payload_sfa.get(), 65536) catch
unreachable; // enough space
defer hot_update_payload.deinit();
Expand Down Expand Up @@ -2976,7 +2976,7 @@ fn startNextBundleIfPresent(dev: *DevServer) void {

// If there were pending requests, begin another bundle.
if (dev.next_bundle.reload_event != null or dev.next_bundle.requests.first != null or dev.next_bundle.promise.strong.hasValue()) {
var sfb = std.heap.stackFallback(4096, dev.allocator());
var sfb = bun.allocators.stackFallback(4096, dev.allocator());
const temp_alloc = sfb.get();
var entry_points: EntryPointList = .empty;
defer entry_points.deinit(temp_alloc);
Expand Down Expand Up @@ -3610,7 +3610,7 @@ pub fn emitVisualizerMessageIfNeeded(dev: *DevServer) void {
defer dev.emitMemoryVisualizerMessageIfNeeded();
if (dev.emit_incremental_visualizer_events == 0) return;

var sfb = std.heap.stackFallback(65536, dev.allocator());
var sfb = bun.allocators.stackFallback(65536, dev.allocator());
var payload = std.ArrayList(u8).initCapacity(sfb.get(), 65536) catch
unreachable; // enough capacity on the stack
defer payload.deinit();
Expand Down Expand Up @@ -3640,7 +3640,7 @@ pub fn emitMemoryVisualizerMessage(dev: *DevServer) void {
comptime assert(bun.FeatureFlags.bake_debugging_features);
bun.debugAssert(dev.emit_memory_visualizer_events > 0);

var sfb = std.heap.stackFallback(65536, dev.allocator());
var sfb = bun.allocators.stackFallback(65536, dev.allocator());
var payload = std.ArrayList(u8).initCapacity(sfb.get(), 65536) catch
unreachable; // enough capacity on the stack
defer payload.deinit();
Expand Down Expand Up @@ -4215,7 +4215,7 @@ fn dumpStateDueToCrash(dev: *DevServer) !void {
try file.writeAll(start);
try file.writeAll("\nlet inlinedData = Uint8Array.from(atob(\"");

var sfb = std.heap.stackFallback(4096, dev.allocator());
var sfb = bun.allocators.stackFallback(4096, dev.allocator());
var payload = try std.ArrayList(u8).initCapacity(sfb.get(), 4096);
defer payload.deinit();
try dev.writeVisualizerMessage(&payload);
Expand Down
4 changes: 2 additions & 2 deletions src/bake/DevServer/ErrorReportRequest.zig
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ pub fn runWithBody(ctx: *ErrorReportRequest, body: []const u8, r: AnyResponse) !
var s = std.io.fixedBufferStream(body);
const reader = s.reader();

var sfa_general = std.heap.stackFallback(65536, ctx.dev.allocator());
var sfa_sourcemap = std.heap.stackFallback(65536, ctx.dev.allocator());
var sfa_general = bun.allocators.stackFallback(65536, ctx.dev.allocator());
var sfa_sourcemap = bun.allocators.stackFallback(65536, ctx.dev.allocator());
const temp_alloc = sfa_general.get();
var arena = std.heap.ArenaAllocator.init(temp_alloc);
defer arena.deinit();
Expand Down
2 changes: 1 addition & 1 deletion src/bake/DevServer/HotReloadEvent.zig
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ pub fn run(first: *HotReloadEvent) void {
return;
}

var sfb = std.heap.stackFallback(4096, dev.allocator());
var sfb = bun.allocators.stackFallback(4096, dev.allocator());
const temp_alloc = sfb.get();
var entry_points: EntryPointList = .empty;
defer entry_points.deinit(temp_alloc);
Expand Down
4 changes: 2 additions & 2 deletions src/bake/DevServer/IncrementalGraph.zig
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,7 @@ pub fn IncrementalGraph(comptime side: bake.Side) type {
bun.assert(bundler_index.isValid());
bun.assert(ctx.loaders[bundler_index.get()].isCSS());

var sfb = std.heap.stackFallback(@sizeOf(bun.ast.Index) * 64, temp_alloc);
var sfb = bun.allocators.stackFallback(@sizeOf(bun.ast.Index) * 64, temp_alloc);
const queue_alloc = sfb.get();

// This queue avoids stack overflow.
Expand Down Expand Up @@ -1728,7 +1728,7 @@ pub fn IncrementalGraph(comptime side: bake.Side) type {
// to inform the HMR runtime some crucial entry-point info. The
// exact upper bound of this can be calculated, but is not to
// avoid worrying about windows paths.
var end_sfa = std.heap.stackFallback(65536, g.allocator());
var end_sfa = bun.allocators.stackFallback(65536, g.allocator());
var end_list = std.ArrayList(u8).initCapacity(end_sfa.get(), 65536) catch unreachable;
defer end_list.deinit();
const end = end: {
Expand Down
4 changes: 2 additions & 2 deletions src/bake/DevServer/SerializedFailure.zig
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ pub fn initFromJs(dev: *DevServer, owner: Owner, value: JSValue) !SerializedFail
@panic("TODO");
}
// Avoid small re-allocations without requesting so much from the heap
var sfb = std.heap.stackFallback(65536, dev.allocator());
var sfb = bun.allocators.stackFallback(65536, dev.allocator());
var payload = std.ArrayList(u8).initCapacity(sfb.get(), 65536) catch
unreachable; // enough space
const w = payload.writer();
Expand All @@ -137,7 +137,7 @@ pub fn initFromLog(
assert(messages.len > 0);

// Avoid small re-allocations without requesting so much from the heap
var sfb = std.heap.stackFallback(65536, dev.allocator());
var sfb = bun.allocators.stackFallback(65536, dev.allocator());
var payload = std.ArrayList(u8).initCapacity(sfb.get(), 65536) catch
unreachable; // enough space
const w = payload.writer();
Expand Down
6 changes: 3 additions & 3 deletions src/bake/FrameworkRouter.zig
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ pub const Style = union(enum) {
pub fn fromJS(value: JSValue, global: *jsc.JSGlobalObject) !Style {
if (value.isString()) {
const bun_string = try value.toBunString(global);
var sfa = std.heap.stackFallback(4096, bun.default_allocator);
var sfa = bun.allocators.stackFallback(4096, bun.default_allocator);
const utf8 = bun_string.toUTF8(sfa.get());
defer utf8.deinit();
if (map.get(utf8.slice())) |style| {
Expand Down Expand Up @@ -1241,7 +1241,7 @@ pub const JSFrameworkRouter = struct {

var params_out: MatchedParams = undefined;
if (jsfr.router.matchSlow(path_slice.slice(), &params_out)) |index| {
var sfb = std.heap.stackFallback(4096, bun.default_allocator);
var sfb = bun.allocators.stackFallback(4096, bun.default_allocator);
const alloc = sfb.get();

return (try jsc.JSObject.create(.{
Expand All @@ -1264,7 +1264,7 @@ pub const JSFrameworkRouter = struct {
pub fn toJSON(jsfr: *JSFrameworkRouter, global: *JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
_ = callframe;

var sfb = std.heap.stackFallback(4096, bun.default_allocator);
var sfb = bun.allocators.stackFallback(4096, bun.default_allocator);
const alloc = sfb.get();

return jsfr.routeToJson(global, Route.Index.init(0), alloc);
Expand Down
8 changes: 4 additions & 4 deletions src/bake/production.zig
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,7 @@ pub export fn BakeToWindowsPath(input: bun.String) callconv(.C) bun.String {
if (comptime bun.Environment.isPosix) {
@panic("This code should not be called on POSIX systems.");
}
var sfa = std.heap.stackFallback(1024, bun.default_allocator);
var sfa = bun.allocators.stackFallback(1024, bun.default_allocator);
const alloc = sfa.get();
const input_utf8 = input.toUTF8(alloc);
defer input_utf8.deinit();
Expand All @@ -807,7 +807,7 @@ pub export fn BakeToWindowsPath(input: bun.String) callconv(.C) bun.String {
}

pub export fn BakeProdResolve(global: *jsc.JSGlobalObject, a_str: bun.String, specifier_str: bun.String) callconv(.C) bun.String {
var sfa = std.heap.stackFallback(@sizeOf(bun.PathBuffer) * 2, bun.default_allocator);
var sfa = bun.allocators.stackFallback(@sizeOf(bun.PathBuffer) * 2, bun.default_allocator);
const alloc = sfa.get();

const specifier = specifier_str.toUTF8(alloc);
Expand Down Expand Up @@ -1020,7 +1020,7 @@ pub const PerThread = struct {

/// Given a key, returns the source code to load.
pub export fn BakeProdLoad(pt: *PerThread, key: bun.String) bun.String {
var sfa = std.heap.stackFallback(4096, bun.default_allocator);
var sfa = bun.allocators.stackFallback(4096, bun.default_allocator);
const allocator = sfa.get();
const utf8 = key.toUTF8(allocator);
defer utf8.deinit();
Expand All @@ -1033,7 +1033,7 @@ pub export fn BakeProdLoad(pt: *PerThread, key: bun.String) bun.String {
}

pub export fn BakeProdSourceMap(pt: *PerThread, key: bun.String) bun.String {
var sfa = std.heap.stackFallback(4096, bun.default_allocator);
var sfa = bun.allocators.stackFallback(4096, bun.default_allocator);
const allocator = sfa.get();
const utf8 = key.toUTF8(allocator);
defer utf8.deinit();
Expand Down
2 changes: 1 addition & 1 deletion src/bun.js/ConsoleObject.zig
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,7 @@ pub const TablePrinter = struct {
) !void {
const globalObject = this.globalObject;

var stack_fallback = std.heap.stackFallback(@sizeOf(Column) * 16, this.globalObject.allocator());
var stack_fallback = bun.allocators.stackFallback(@sizeOf(Column) * 16, this.globalObject.allocator());
var columns = try std.ArrayList(Column).initCapacity(stack_fallback.get(), 16);
defer {
for (columns.items) |*col| {
Expand Down
Loading
Loading