mirror of
https://github.com/oven-sh/bun
synced 2026-02-17 14:22:01 +00:00
perf(linux): add memfd optimizations and typed flags (#25597)
## Summary
- Add `MemfdFlags` enum to replace raw integer flags for `memfd_create`,
providing semantic clarity for different use cases (`executable`,
`non_executable`, `cross_process`)
- Add support for `MFD_EXEC` and `MFD_NOEXEC_SEAL` flags (Linux 6.3+)
with automatic fallback to older kernel flags when `EINVAL` is returned
- Use memfd + `/proc/self/fd/{fd}` path for loading embedded `.node`
files in standalone builds, avoiding disk writes entirely on Linux
## Test plan
- [ ] Verify standalone builds with embedded `.node` files work on Linux
- [ ] Verify fallback works on older kernels (pre-6.3)
- [ ] Verify subprocess stdio memfd still works correctly
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -30,19 +30,45 @@ pub fn resetArena(this: *ModuleLoader, jsc_vm: *VirtualMachine) void {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolveEmbeddedFile(vm: *VirtualMachine, input_path: []const u8, extname: []const u8) ?[]const u8 {
|
||||
fn resolveEmbeddedNodeFileViaMemfd(file: *bun.StandaloneModuleGraph.File, path_buffer: *bun.PathBuffer, fd: *i32) ![]const u8 {
|
||||
var label_buf: [128]u8 = undefined;
|
||||
const count = struct {
|
||||
pub var counter = std.atomic.Value(u32).init(0);
|
||||
pub fn get() u32 {
|
||||
return counter.fetchAdd(1, .seq_cst);
|
||||
}
|
||||
}.get();
|
||||
const label = std.fmt.bufPrintZ(&label_buf, "node-addon-{d}", .{count}) catch "";
|
||||
const memfd = try bun.sys.memfd_create(label, .executable).unwrap();
|
||||
errdefer memfd.close();
|
||||
|
||||
fd.* = @intCast(memfd.cast());
|
||||
errdefer fd.* = -1;
|
||||
|
||||
try bun.sys.ftruncate(memfd, @intCast(file.contents.len)).unwrap();
|
||||
try bun.sys.File.writeAll(.{ .handle = memfd }, file.contents).unwrap();
|
||||
|
||||
return try std.fmt.bufPrint(path_buffer, "/proc/self/fd/{d}", .{memfd.cast()});
|
||||
}
|
||||
|
||||
pub fn resolveEmbeddedFile(vm: *VirtualMachine, path_buf: *bun.PathBuffer, linux_memfd: *i32, input_path: []const u8, extname: []const u8) ?[]const u8 {
|
||||
if (input_path.len == 0) return null;
|
||||
var graph = vm.standalone_module_graph orelse return null;
|
||||
const file = graph.find(input_path) orelse return null;
|
||||
|
||||
if (comptime Environment.isLinux) {
|
||||
// TODO: use /proc/fd/12346 instead! Avoid the copy!
|
||||
// Best-effort: use memfd to avoid hitting the disk
|
||||
if (resolveEmbeddedNodeFileViaMemfd(file, path_buf, linux_memfd)) |path| {
|
||||
return path;
|
||||
} else |_| {
|
||||
// fall back to temp file
|
||||
}
|
||||
}
|
||||
|
||||
// atomically write to a tmpfile and then move it to the final destination
|
||||
var tmpname_buf: bun.PathBuffer = undefined;
|
||||
const tmpfilename = bun.fs.FileSystem.tmpname(extname, &tmpname_buf, bun.hash(file.name)) catch return null;
|
||||
|
||||
const tmpname_buf = bun.path_buffer_pool.get();
|
||||
defer bun.path_buffer_pool.put(tmpname_buf);
|
||||
const tmpfilename = bun.fs.FileSystem.tmpname(extname, tmpname_buf, bun.hash(file.name)) catch return null;
|
||||
const tmpdir: bun.FD = .fromStdDir(bun.fs.FileSystem.instance.tmpdir() catch return null);
|
||||
|
||||
// First we open the tmpfile, to avoid any other work in the event of failure.
|
||||
@@ -50,7 +76,7 @@ pub fn resolveEmbeddedFile(vm: *VirtualMachine, input_path: []const u8, extname:
|
||||
defer tmpfile.fd.close();
|
||||
|
||||
switch (bun.api.node.fs.NodeFS.writeFileWithPathBuffer(
|
||||
&tmpname_buf, // not used
|
||||
tmpname_buf, // not used
|
||||
|
||||
.{
|
||||
.data = .{
|
||||
@@ -66,7 +92,7 @@ pub fn resolveEmbeddedFile(vm: *VirtualMachine, input_path: []const u8, extname:
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return bun.path.joinAbs(bun.fs.FileSystem.instance.fs.tmpdirPath(), .auto, tmpfilename);
|
||||
return bun.path.joinAbsStringBuf(bun.fs.FileSystem.instance.fs.tmpdirPath(), path_buf, &[_]string{tmpfilename}, .auto);
|
||||
}
|
||||
|
||||
pub export fn Bun__getDefaultLoader(global: *JSGlobalObject, str: *const bun.String) api.Loader {
|
||||
@@ -1300,12 +1326,14 @@ pub const FetchFlags = enum {
|
||||
};
|
||||
|
||||
/// Support embedded .node files
|
||||
export fn Bun__resolveEmbeddedNodeFile(vm: *VirtualMachine, in_out_str: *bun.String) bool {
|
||||
export fn Bun__resolveEmbeddedNodeFile(vm: *VirtualMachine, in_out_str: *bun.String, linux_memfd_fd_to_close: *i32) bool {
|
||||
if (vm.standalone_module_graph == null) return false;
|
||||
|
||||
const input_path = in_out_str.toUTF8(bun.default_allocator);
|
||||
defer input_path.deinit();
|
||||
const result = ModuleLoader.resolveEmbeddedFile(vm, input_path.slice(), "node") orelse return false;
|
||||
const path_buffer = bun.path_buffer_pool.get();
|
||||
defer bun.path_buffer_pool.put(path_buffer);
|
||||
const result = ModuleLoader.resolveEmbeddedFile(vm, path_buffer, linux_memfd_fd_to_close, input_path.slice(), "node") orelse return false;
|
||||
in_out_str.* = bun.String.cloneUTF8(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user