Compare commits

...

9 Commits

Author SHA1 Message Date
Jarred SUmner
d2be50bf4d Merge branch 'main' of github.com:Jarred-Sumner/bun into main 2021-10-04 20:42:17 -07:00
Jarred SUmner
f0f3d6d4eb Statically link libicu on Linux so it doesn't require installing icu separately.
this doubles the binary size :(
2021-10-04 20:41:27 -07:00
Jarred SUmner
21d918921a memfd experiment did not yield perf gains on Linux
I suspect the reason why is because we were already using tmpfs. So it was already writing to an in-memory file. O_TMPFILE doesn't seem to do anything for us either here.
2021-10-04 20:01:05 -07:00
Jarred Sumner
55095edee6 Don't pass O_EXCL 2021-10-04 19:07:19 -07:00
Jarred Sumner
6a7deab74d O_TMPFILE 2021-10-04 19:00:18 -07:00
Jarred Sumner
7db27f11d0 memfd_create does not actually make it faster 2021-10-04 18:58:31 -07:00
Jarred Sumner
3ebb4feb98 Attempt to optimize bun bun on Linux by using memfd_create and copying the resulting file via sendfile() 2021-10-04 18:37:51 -07:00
Jarred Sumner
e8dab9bfcf Fix moving files across mount points 2021-10-04 15:35:08 -07:00
Jarred Sumner
dc3eee44a9 Bump 2021-10-04 04:54:23 -07:00
7 changed files with 140 additions and 16 deletions

View File

@@ -298,9 +298,14 @@ MACOS_ICU_INCLUDE := $(HOMEBREW_PREFIX)opt/icu4c/include
ICU_FLAGS :=
# TODO: find a way to make this more resilient
# Ideally, we could just look up the linker search paths
LIB_ICU_PATH ?= /usr/lib/x86_64-linux-gnu
ifeq ($(OS_NAME),linux)
ICU_FLAGS += -licuuc -licudata -licui18n
ICU_FLAGS += $(LIB_ICU_PATH)/libicuuc.a $(LIB_ICU_PATH)/libicudata.a $(LIB_ICU_PATH)/libicui18n.a
endif
ifeq ($(OS_NAME),darwin)
ICU_FLAGS += -l icucore \
$(MACOS_ICU_FILES) \

View File

@@ -1 +1 @@
30
32

View File

@@ -4,5 +4,5 @@
},
"name": "bun-cli-darwin-x64",
"repository": "https://github.com/jarred-sumner/bun",
"version": "0.0.30"
"version": "0.0.31"
}

View File

@@ -9,5 +9,5 @@
"postinstall": "node postinstall.js",
"prepublishOnly": "rm -rf ./bin/bun; chmod +x ./reset-bin.js; cp ./reset-bin.js ./bin/bun"
},
"version": "0.0.30"
"version": "0.0.31"
}

View File

@@ -700,15 +700,13 @@ pub const Bundler = struct {
const tmpname = try bundler.fs.tmpname(
".bun",
std.mem.span(&tmpname_buf),
std.hash.Wyhash.hash(0, std.mem.span(destination)),
std.hash.Wyhash.hash(@intCast(usize, std.time.milliTimestamp()) % std.math.maxInt(u32), std.mem.span(destination)),
);
var tmpfile = try tmpdir.createFileZ(tmpname, .{ .read = isDebug, .exclusive = true });
var tmpfile = Fs.FileSystem.RealFS.Tmpfile{};
try tmpfile.create(&bundler.fs.fs, tmpname);
errdefer {
tmpfile.close();
tmpdir.deleteFile(std.mem.span(tmpname)) catch {};
}
errdefer tmpfile.closeAndDelete(tmpname);
var generator = try allocator.create(GenerateNodeModuleBundle);
var queue = try BunQueue.init(allocator);
@@ -722,7 +720,7 @@ pub const Bundler = struct {
.estimated_input_lines_of_code = 0,
// .resolve_queue = queue,
.bundler = bundler,
.tmpfile = tmpfile,
.tmpfile = tmpfile.file(),
.dynamic_import_file_size_store = U32Map.init(allocator),
.dynamic_import_file_size_store_lock = Lock.init(),
@@ -930,8 +928,7 @@ pub const Bundler = struct {
// }
if (this.log.errors > 0) {
tmpfile.close();
tmpdir.deleteFile(std.mem.span(tmpname)) catch {};
tmpfile.closeAndDelete(std.mem.span(tmpname));
// We stop here because if there are errors we don't know if the bundle is valid
// This manifests as a crash when sorting through the module list because we may have added files to the bundle which were never actually finished being added.
return null;
@@ -1112,7 +1109,7 @@ pub const Bundler = struct {
// chmod 777
0000010 | 0000100 | 0000001 | 0001000 | 0000040 | 0000004 | 0000002 | 0000400 | 0000200 | 0000020,
);
try std.os.renameatZ(tmpdir.fd, tmpname, top_dir.fd, destination);
try tmpfile.promote(tmpname, top_dir.fd, destination);
// Print any errors at the end
// try this.log.print(Output.errorWriter());
return javascript_bundle_container;

View File

@@ -1,4 +1,5 @@
const std = @import("std");
const Enviroment = @import("./env.zig");
pub usingnamespace switch (std.Target.current.os.tag) {
.macos => @import("./darwin_c.zig"),
@@ -16,10 +17,10 @@ const errno = os.errno;
const zeroes = mem.zeroes;
pub extern "c" fn chmod([*c]const u8, mode_t) c_int;
pub extern "c" fn fchmod(c_int, mode_t) c_int;
pub extern "c" fn fchmod(std.c.fd_t, mode_t) c_int;
pub extern "c" fn umask(mode_t) mode_t;
pub extern "c" fn fchmodat(c_int, [*c]const u8, mode_t, c_int) c_int;
pub extern "c" fn fchown(std.c.fd_t, std.c.uid_t, std.c.gid_t) c_int;
pub extern "c" fn lstat([*c]const u8, [*c]libc_stat) c_int;
pub extern "c" fn lstat64([*c]const u8, [*c]libc_stat) c_int;
@@ -89,3 +90,77 @@ pub fn lstat_absolute(path: [:0]const u8) StatError!Stat {
.ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
};
}
// renameatZ fails when renaming across mount points
// we assume that this is relatively uncommon
pub fn moveFileZ(from_dir: std.os.fd_t, filename: [*:0]const u8, to_dir: std.os.fd_t, destination: [*:0]const u8) !void {
std.os.renameatZ(from_dir, filename, to_dir, destination) catch |err| {
switch (err) {
error.RenameAcrossMountPoints => {
try moveFileZSlow(from_dir, filename, to_dir, destination);
},
else => {
return err;
},
}
};
}
pub fn moveFileZWithHandle(from_handle: std.os.fd_t, from_dir: std.os.fd_t, filename: [*:0]const u8, to_dir: std.os.fd_t, destination: [*:0]const u8) !void {
std.os.renameatZ(from_dir, filename, to_dir, destination) catch |err| {
switch (err) {
error.RenameAcrossMountPoints => {
try moveFileZSlowWithHandle(from_handle, to_dir, destination);
},
else => {
return err;
},
}
};
}
// On Linux, this will be fast because sendfile() supports copying between two file descriptors on disk
// macOS & BSDs will be slow because
pub fn moveFileZSlow(from_dir: std.os.fd_t, filename: [*:0]const u8, to_dir: std.os.fd_t, destination: [*:0]const u8) !void {
const in_handle = try std.os.openatZ(from_dir, filename, std.os.O_RDONLY | std.os.O_CLOEXEC, 0600);
try moveFileZSlowWithHandle(in_handle, to_dir, destination);
}
pub fn moveFileZSlowWithHandle(in_handle: std.os.fd_t, to_dir: std.os.fd_t, destination: [*:0]const u8) !void {
const stat = try std.os.fstat(in_handle);
// delete if exists, don't care if it fails. it may fail due to the file not existing
// delete here because we run into weird truncation issues if we do not
// ftruncate() instead didn't work.
// this is technically racy because it could end up deleting the file without saving
std.os.unlinkatZ(to_dir, destination, 0) catch {};
const out_handle = try std.os.openatZ(to_dir, destination, std.os.O_WRONLY | std.os.O_CREAT | std.os.O_CLOEXEC, 022);
defer std.os.close(out_handle);
if (comptime Enviroment.isLinux) {
_ = std.os.system.fallocate(out_handle, 0, 0, @intCast(i64, stat.size));
_ = try std.os.sendfile(out_handle, in_handle, 0, @intCast(usize, stat.size), &[_]std.c.iovec_const{}, &[_]std.c.iovec_const{}, 0);
} else {
if (comptime Enviroment.isMac) {
// if this fails, it doesn't matter
// we only really care about read & write succeeding
preallocate_file(
out_handle,
@intCast(std.os.off_t, 0),
@intCast(std.os.off_t, stat.size),
) catch {};
}
var buf: [8092 * 2]u8 = undefined;
var total_read: usize = 0;
while (true) {
const read = try std.os.pread(in_handle, &buf, total_read);
total_read += read;
if (read == 0) break;
const bytes = buf[0..read];
const written = try std.os.write(out_handle, bytes);
if (written == 0) break;
}
}
_ = fchmod(out_handle, stat.mode);
_ = fchown(out_handle, stat.uid, stat.gid);
}

View File

@@ -513,6 +513,53 @@ pub const FileSystem = struct {
return file;
}
pub const Tmpfile = struct {
fd: std.os.fd_t = 0,
dir_fd: std.os.fd_t = 0,
pub inline fn dir(this: *Tmpfile) std.fs.Dir {
return std.fs.Dir{
.fd = this.dir_fd,
};
}
pub inline fn file(this: *Tmpfile) std.fs.File {
return std.fs.File{
.handle = this.fd,
};
}
pub fn close(this: *Tmpfile) void {
if (this.fd != 0) std.os.close(this.fd);
}
pub fn create(this: *Tmpfile, rfs: *RealFS, name: [*:0]const u8) !void {
var tmpdir_ = try rfs.openTmpDir();
const flags = std.os.O_CREAT | std.os.O_RDWR | std.os.O_CLOEXEC;
this.dir_fd = tmpdir_.fd;
this.fd = try std.os.openatZ(tmpdir_.fd, name, flags, std.os.S_IRWXO);
}
pub fn promote(this: *Tmpfile, from_name: [*:0]const u8, destination_fd: std.os.fd_t, name: [*:0]const u8) !void {
std.debug.assert(this.fd != 0);
std.debug.assert(this.dir_fd != 0);
try C.moveFileZWithHandle(this.fd, this.dir_fd, from_name, destination_fd, name);
this.close();
}
pub fn closeAndDelete(this: *Tmpfile, name: [*:0]const u8) void {
this.close();
if (comptime !Environment.isLinux) {
if (this.dir_fd == 0) return;
this.dir().deleteFileZ(name) catch {};
}
}
};
inline fn _fetchCacheFile(fs: *RealFS, basename: string) !std.fs.File {
var parts = [_]string{ "node_modules", ".cache", basename };
var path = fs.parent_fs.join(&parts);