aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Jarred SUmner <jarred@jarredsumner.com> 2021-10-02 22:54:19 -0700
committerGravatar Jarred SUmner <jarred@jarredsumner.com> 2021-10-02 22:54:19 -0700
commitcdabcfd9d09d2f4928ba7a918f654e001bb3c2eb (patch)
tree46141053ec9163fc9abb087e57b7819c3716a4d6 /src
parentac7a96b088bce9e9703e6501a0546357b3a33faf (diff)
downloadbun-cdabcfd9d09d2f4928ba7a918f654e001bb3c2eb.tar.gz
bun-cdabcfd9d09d2f4928ba7a918f654e001bb3c2eb.tar.zst
bun-cdabcfd9d09d2f4928ba7a918f654e001bb3c2eb.zip
Linux works now.
Diffstat (limited to 'src')
-rw-r--r--src/bundler.zig2
-rw-r--r--src/css_scanner.zig5
-rw-r--r--src/fs.zig26
-rw-r--r--src/global.zig9
-rw-r--r--src/http.zig54
-rw-r--r--src/javascript/jsc/JavascriptCore.zig4
m---------src/javascript/jsc/WebKit0
-rw-r--r--src/javascript/jsc/bindings/ZigGlobalObject.cpp17
-rw-r--r--src/main.zig1
-rw-r--r--src/runtime.version2
-rw-r--r--src/watcher.zig95
11 files changed, 141 insertions, 74 deletions
diff --git a/src/bundler.zig b/src/bundler.zig
index cc82ccf41..262a22888 100644
--- a/src/bundler.zig
+++ b/src/bundler.zig
@@ -2920,7 +2920,7 @@ pub const Transformer = struct {
var arena: std.heap.ArenaAllocator = undefined;
const use_arenas = opts.entry_points.len > 8;
- var ulimit: usize = Fs.FileSystem.RealFS.adjustUlimit();
+ var ulimit: usize = Fs.FileSystem.RealFS.adjustUlimit() catch unreachable;
var care_about_closing_files = !(FeatureFlags.store_file_descriptors and opts.entry_points.len * 2 < ulimit);
var transformer = Transformer{
diff --git a/src/css_scanner.zig b/src/css_scanner.zig
index 0adcd9917..41e43c22e 100644
--- a/src/css_scanner.zig
+++ b/src/css_scanner.zig
@@ -1263,7 +1263,12 @@ pub fn NewBundler(
if (watcher_index == null) {
var file = try std.fs.openFileAbsolute(absolute_path, .{ .read = true });
+
try this.watcher.appendFile(file.handle, absolute_path, hash, .css, 0, null, true);
+ if (this.watcher.watchloop_handle == null) {
+ try this.watcher.start();
+ }
+
}
try this.import_queue.writeItem(hash);
diff --git a/src/fs.zig b/src/fs.zig
index 517dcdbd5..2313dc17b 100644
--- a/src/fs.zig
+++ b/src/fs.zig
@@ -541,16 +541,22 @@ pub const FileSystem = struct {
}
// Always try to max out how many files we can keep open
- pub fn adjustUlimit() usize {
- var limit = std.os.getrlimit(.NOFILE) catch return 32;
- if (limit.cur < limit.max) {
- var new_limit = std.mem.zeroes(std.os.rlimit);
- new_limit.cur = limit.max;
- new_limit.max = limit.max;
- std.os.setrlimit(.NOFILE, new_limit) catch return limit.cur;
- return new_limit.cur;
+ pub fn adjustUlimit() !usize {
+ const LIMITS = [_]std.os.rlimit_resource{std.os.rlimit_resource.STACK, std.os.rlimit_resource.NOFILE};
+ inline for (LIMITS) |limit_type, i| {
+ const limit = try std.os.getrlimit(limit_type);
+
+ if (limit.cur < limit.max) {
+ var new_limit = std.mem.zeroes(std.os.rlimit);
+ new_limit.cur = limit.max;
+ new_limit.max = limit.max;
+
+ try std.os.setrlimit(limit_type, new_limit);
+
+ }
+
+ if (i == LIMITS.len - 1) return limit.max;
}
- return limit.cur;
}
var _entries_option_map: *EntriesOption.Map = undefined;
@@ -559,7 +565,7 @@ pub const FileSystem = struct {
allocator: *std.mem.Allocator,
cwd: string,
) RealFS {
- const file_limit = adjustUlimit();
+ const file_limit = adjustUlimit() catch unreachable;
if (!_entries_option_map_loaded) {
_entries_option_map = EntriesOption.Map.init(allocator);
diff --git a/src/global.zig b/src/global.zig
index 236751a6b..64d5f1a82 100644
--- a/src/global.zig
+++ b/src/global.zig
@@ -1,10 +1,15 @@
const std = @import("std");
pub usingnamespace @import("strings.zig");
+pub const Environment = @import("env.zig");
+
-pub const default_allocator: *std.mem.Allocator = if (isTest) std.heap.c_allocator else @import("./memory_allocator.zig").c_allocator;
+pub const default_allocator: *std.mem.Allocator = if (isTest or Environment.isLinux)
+ std.heap.c_allocator
+ else
+ @import("./memory_allocator.zig").c_allocator;
pub const C = @import("c.zig");
-pub const Environment = @import("env.zig");
+
pub usingnamespace Environment;
pub const FeatureFlags = @import("feature_flags.zig");
diff --git a/src/http.zig b/src/http.zig
index 0ddc02540..72a452327 100644
--- a/src/http.zig
+++ b/src/http.zig
@@ -50,6 +50,7 @@ threadlocal var req_headers_buf: [100]picohttp.Header = undefined;
threadlocal var res_headers_buf: [100]picohttp.Header = undefined;
const sync = @import("./sync.zig");
const JavaScript = @import("./javascript/jsc/javascript.zig");
+const JavaScriptCore = @import("./javascript/jsc/JavascriptCore.zig");
usingnamespace @import("./javascript/jsc/bindings/bindings.zig");
usingnamespace @import("./javascript/jsc/bindings/exports.zig");
const Router = @import("./router.zig");
@@ -1102,11 +1103,11 @@ pub const RequestContext = struct {
pub var channel: Channel = undefined;
var has_loaded_channel = false;
pub var javascript_disabled = false;
-
+ var js_thread: std.Thread = undefined;
pub fn spawnThread(handler: *HandlerThread) !void {
- var thread = try std.Thread.spawn(.{}, spawn, .{handler});
- thread.setName("WebSocket") catch {};
- thread.detach();
+ js_thread = try std.Thread.spawn(.{.stack_size = 64 * 1024 * 1024}, spawn, .{handler});
+ js_thread.setName("JavaScript SSR") catch {};
+ js_thread.detach();
}
pub fn spawn(handler: *HandlerThread) void {
@@ -1118,20 +1119,19 @@ pub const RequestContext = struct {
javascript_disabled = true;
}
var start_timer = std.time.Timer.start() catch unreachable;
+
var stdout = std.io.getStdOut();
- // var stdout = std.io.bufferedWriter(stdout_file.writer());
var stderr = std.io.getStdErr();
- // var stderr = std.io.bufferedWriter(stderr_file.writer());
var output_source = Output.Source.init(stdout, stderr);
- // defer stdout.flush() catch {};
- // defer stderr.flush() catch {};
+ defer Output.flush();
Output.Source.set(&output_source);
+
js_ast.Stmt.Data.Store.create(std.heap.c_allocator);
js_ast.Expr.Data.Store.create(std.heap.c_allocator);
- defer Output.flush();
+
var vm = JavaScript.VirtualMachine.init(
std.heap.c_allocator,
handler.args,
@@ -1947,6 +1947,8 @@ pub const RequestContext = struct {
// CSS handles this specially
if (loader != .css and client_entry_point_ == null) {
if (written.input_fd) |written_fd| {
+
+
try ctx.watcher.addFile(
written_fd,
result.file.input.text,
@@ -1958,8 +1960,9 @@ pub const RequestContext = struct {
);
if (ctx.watcher.watchloop_handle == null) {
- try ctx.watcher.start();
+ ctx.watcher.start() catch {};
}
+
}
} else {
if (written.written > 0) {
@@ -2008,6 +2011,7 @@ pub const RequestContext = struct {
if (file.autowatch) {
// we must never autowatch a file that will be closed
std.debug.assert(!file.close_handle_on_complete);
+
if (ctx.watcher.addFile(
file.fd,
result.file.input.text,
@@ -2438,16 +2442,6 @@ pub const Server = struct {
transform_options: Api.TransformOptions,
javascript_enabled: bool = false,
- pub fn adjustUlimit() !void {
- var limit = try std.os.getrlimit(.NOFILE);
- if (limit.cur < limit.max) {
- var new_limit = std.mem.zeroes(std.os.rlimit);
- new_limit.cur = limit.max;
- new_limit.max = limit.max;
- try std.os.setrlimit(.NOFILE, new_limit);
- }
- }
-
pub fn onTCPConnection(server: *Server, conn: tcp.Connection, comptime features: ConnectionFeatures) void {
conn.client.setNoDelay(true) catch {};
conn.client.setQuickACK(true) catch {};
@@ -2502,6 +2496,9 @@ pub const Server = struct {
defer ctx.watcher.flushEvictions();
defer Output.flush();
+ var rfs: *Fs.FileSystem.RealFS = &ctx.bundler.fs.fs;
+
+
// It's important that this function does not do any memory allocations
// If this blocks, it can cause cascading bad things to happen
for (events) |event| {
@@ -2526,7 +2523,6 @@ pub const Server = struct {
switch (kind) {
.file => {
if (event.op.delete or event.op.rename) {
- var rfs: *Fs.FileSystem.RealFS = &ctx.bundler.fs.fs;
ctx.watcher.removeAtIndex(
event.index,
0,
@@ -2559,12 +2555,12 @@ pub const Server = struct {
}
},
.directory => {
- var rfs: *Fs.FileSystem.RealFS = &ctx.bundler.fs.fs;
+
rfs.bustEntriesCache(file_path);
ctx.bundler.resolver.dir_cache.remove(file_path);
- if (event.op.delete or event.op.rename)
- ctx.watcher.removeAtIndex(event.index, hashes[event.index], parent_hashes, .directory);
+ // if (event.op.delete or event.op.rename)
+ // ctx.watcher.removeAtIndex(event.index, hashes[event.index], parent_hashes, .directory);
if (comptime is_emoji_enabled) {
Output.prettyln("<r>📁 <d>Dir change: {s}<r>", .{ctx.bundler.fs.relativeTo(file_path)});
@@ -2577,7 +2573,7 @@ pub const Server = struct {
}
fn run(server: *Server, comptime features: ConnectionFeatures) !void {
- adjustUlimit() catch {};
+ _ = Fs.FileSystem.RealFS.adjustUlimit() catch {};
RequestContext.WebsocketHandler.open_websockets = @TypeOf(
RequestContext.WebsocketHandler.open_websockets,
).init(server.allocator);
@@ -2723,6 +2719,7 @@ pub const Server = struct {
};
};
+ threadlocal var req_ctx_: RequestContext = undefined;
pub fn handleConnection(server: *Server, conn: *tcp.Connection, comptime features: ConnectionFeatures) void {
// https://stackoverflow.com/questions/686217/maximum-on-http-header-values
@@ -2745,9 +2742,9 @@ pub const Server = struct {
var request_arena = server.allocator.create(std.heap.ArenaAllocator) catch unreachable;
request_arena.* = std.heap.ArenaAllocator.init(server.allocator);
- var req_ctx: RequestContext = undefined;
+
- req_ctx = RequestContext.init(
+ req_ctx_ = RequestContext.init(
req,
request_arena,
conn,
@@ -2759,6 +2756,7 @@ pub const Server = struct {
conn.client.deinit();
return;
};
+ var req_ctx = &req_ctx_;
req_ctx.timer.reset();
if (req_ctx.url.needs_redirect) {
@@ -2880,7 +2878,7 @@ pub const Server = struct {
if (comptime features.filesystem_router) {
if (!finished) {
- req_ctx.bundler.router.?.match(server, RequestContext, &req_ctx) catch |err| {
+ req_ctx.bundler.router.?.match(server, RequestContext, req_ctx) catch |err| {
switch (err) {
error.ModuleNotFound => {
req_ctx.sendNotFound() catch {};
diff --git a/src/javascript/jsc/JavascriptCore.zig b/src/javascript/jsc/JavascriptCore.zig
index f443799c1..dd271d2c0 100644
--- a/src/javascript/jsc/JavascriptCore.zig
+++ b/src/javascript/jsc/JavascriptCore.zig
@@ -544,3 +544,7 @@ pub const JSString = struct {
}
}
};
+
+
+// not official api functions
+pub extern "c" fn JSCInitialize() void;
diff --git a/src/javascript/jsc/WebKit b/src/javascript/jsc/WebKit
-Subproject 231267671049bbd7cd60cd6e66fbc76b9e5ee5e
+Subproject 487a7b31de9fa54dab1611799db26907c14dc5a
diff --git a/src/javascript/jsc/bindings/ZigGlobalObject.cpp b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
index dd98742d2..e470e27b5 100644
--- a/src/javascript/jsc/bindings/ZigGlobalObject.cpp
+++ b/src/javascript/jsc/bindings/ZigGlobalObject.cpp
@@ -58,6 +58,8 @@
#include <wtf/text/StringView.h>
#include <wtf/text/WTFString.h>
+#include <wtf/Gigacage.h>
+
#include <cstdlib>
#include <exception>
#include <iostream>
@@ -82,22 +84,21 @@ namespace JSCastingHelpers = JSC::JSCastingHelpers;
bool has_loaded_jsc = false;
-extern "C" JSC__JSGlobalObject *Zig__GlobalObject__create(JSClassRef *globalObjectClass, int count,
- void *console_client) {
-
- if (!has_loaded_jsc) {
+extern "C" void JSCInitialize() {
+ if (has_loaded_jsc) return;
JSC::Options::useSourceProviderCache() = true;
JSC::Options::useUnlinkedCodeBlockJettisoning() = false;
// JSC::Options::useTopLevelAwait() = true;
JSC::Options::exposeInternalModuleLoader() = true;
- std::set_terminate([]() { Zig__GlobalObject__onCrash(); });
+ // std::set_terminate([]() { Zig__GlobalObject__onCrash(); });
WTF::initializeMainThread();
JSC::initialize();
+ // Gigacage::disablePrimitiveGigacage();
has_loaded_jsc = true;
- }
-
- // JSC::Options::useCodeCache() = false;
+}
+extern "C" JSC__JSGlobalObject *Zig__GlobalObject__create(JSClassRef *globalObjectClass, int count,
+ void *console_client) {
auto heapSize = JSC::LargeHeap;
JSC::VM &vm = JSC::VM::create(heapSize).leakRef();
diff --git a/src/main.zig b/src/main.zig
index 7b86f7360..f73f9c292 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -22,6 +22,7 @@ pub fn panic(msg: []const u8, error_return_trace: ?*std.builtin.StackTrace) nore
}
pub var start_time: i128 = 0;
pub fn main() anyerror!void {
+ @import("javascript/jsc/JavascriptCore.zig").JSCInitialize();
start_time = std.time.nanoTimestamp();
// The memory allocator makes a massive difference.
diff --git a/src/runtime.version b/src/runtime.version
index 57659652b..2dcc8c529 100644
--- a/src/runtime.version
+++ b/src/runtime.version
@@ -1 +1 @@
-35057197d4ad54bc \ No newline at end of file
+4d09f9efba49d5ac \ No newline at end of file
diff --git a/src/watcher.zig b/src/watcher.zig
index 63fdb9007..f088d31d2 100644
--- a/src/watcher.zig
+++ b/src/watcher.zig
@@ -60,17 +60,29 @@ pub const INotify = struct {
var eventlist: EventListBuffer = undefined;
var eventlist_ptrs: [128]*const INotifyEvent = undefined;
- const add_mask = IN_EXCL_UNLINK | IN_MOVE_SELF | IN_CREATE | IN_DELETE | IN_DELETE_SELF;
+ var watch_count: std.atomic.Atomic(u32) = std.atomic.Atomic(u32).init(0);
- pub fn watchPath(pathname: [*:0]const u8) !EventListIndex {
+ const watch_file_mask = IN_EXCL_UNLINK | IN_MOVE_SELF | IN_DELETE_SELF | IN_CLOSE_WRITE;
+ const watch_dir_mask = IN_EXCL_UNLINK | IN_DELETE | IN_DELETE_SELF | IN_CREATE | IN_MOVE_SELF | IN_ONLYDIR;
+
+ pub fn watchPath(pathname: [:0]const u8) !EventListIndex {
std.debug.assert(loaded_inotify);
+ const old_count = watch_count.fetchAdd(1, .Release);
+ defer if (old_count == 0) std.Thread.Futex.wake(&watch_count, 10);
+ return std.os.inotify_add_watchZ(inotify_fd, pathname, watch_file_mask);
+ }
- return std.os.inotify_add_watchZ(inotify_fd, pathname, add_mask);
+ pub fn watchDir(pathname: [:0]const u8) !EventListIndex {
+ std.debug.assert(loaded_inotify);
+ const old_count = watch_count.fetchAdd(1, .Release);
+ defer if (old_count == 0) std.Thread.Futex.wake(&watch_count, 10);
+ return std.os.inotify_add_watchZ(inotify_fd, pathname, watch_dir_mask);
}
+
pub fn unwatch(wd: EventListIndex) void {
std.debug.assert(loaded_inotify);
-
+ _ = watch_count.fetchSub(1, .Release);
std.os.inotify_rm_watch(inotify_fd, wd);
}
@@ -84,6 +96,10 @@ pub const INotify = struct {
pub fn read() ![]*const INotifyEvent {
std.debug.assert(loaded_inotify);
+ restart: while (true) {
+
+
+ std.Thread.Futex.wait(&watch_count,0, null) catch unreachable;
const rc = std.os.system.read(
inotify_fd,
@ptrCast([*]u8, @alignCast(@alignOf([*]u8), &eventlist)),
@@ -110,12 +126,14 @@ pub const INotify = struct {
return eventlist_ptrs[0..count];
},
+ .AGAIN => continue :restart,
.INVAL => return error.ShortRead,
.BADF => return error.INotifyFailedToStart,
else => unreachable,
}
+}
unreachable;
}
@@ -183,6 +201,21 @@ pub const WatchEvent = struct {
op: Op,
const KEvent = std.os.Kevent;
+
+ pub const Sorter = void;
+
+ pub fn sortByIndex(context: Sorter, event: WatchEvent, rhs: WatchEvent) bool {
+ return event.index < rhs.index;
+ }
+
+ pub fn merge(this: *WatchEvent, other: WatchEvent) void {
+ this.op = Op{
+ .delete = this.op.delete or other.op.delete,
+ .metadata = this.op.metadata or other.op.metadata,
+ .rename = this.op.rename or other.op.rename,
+ .write = this.op.write or other.op.write,
+ };
+ }
pub fn fromKEvent(this: *WatchEvent, kevent: KEvent) void {
this.* =
@@ -200,13 +233,10 @@ pub const WatchEvent = struct {
pub fn fromINotify(this: *WatchEvent, event: INotify.INotifyEvent, index: WatchItemIndex) void {
this.* = WatchEvent{
.op = Op{
- .delete = (event.mask & INotify.IN_DELETE_SELF) > 0,
- // only applies to directories
- .metadata = (event.mask & INotify.IN_CREATE) > 0 or
- (event.mask & INotify.IN_DELETE) > 0 or
- (event.mask & INotify.IN_MOVE) > 0,
+ .delete = (event.mask & INotify.IN_DELETE_SELF) > 0 or (event.mask & INotify.IN_DELETE) > 0,
+ .metadata = false,
.rename = (event.mask & INotify.IN_MOVE_SELF) > 0,
- .write = (event.mask & INotify.IN_MODIFY) > 0,
+ .write = (event.mask & INotify.IN_MODIFY) > 0 or (event.mask & INotify.IN_MOVE) > 0,
},
.index = index,
};
@@ -257,6 +287,8 @@ pub fn NewWatcher(comptime ContextType: type) type {
pub fn init(ctx: ContextType, fs: *Fs.FileSystem, allocator: *std.mem.Allocator) !*Watcher {
var watcher = try allocator.create(Watcher);
+ try PlatformWatcher.init();
+
watcher.* = Watcher{
.fs = fs,
.fd = 0,
@@ -272,7 +304,6 @@ pub fn NewWatcher(comptime ContextType: type) type {
}
pub fn start(this: *Watcher) !void {
- try PlatformWatcher.init();
std.debug.assert(this.watchloop_handle == null);
var thread = try std.Thread.spawn(.{}, Watcher.watchLoop, .{this});
thread.setName("File Watcher") catch {};
@@ -389,7 +420,7 @@ pub fn NewWatcher(comptime ContextType: type) type {
this.ctx.onFileUpdate(watchevents, this.watchlist);
}
} else if (Environment.isLinux) {
- while (true) {
+ restart: while (true) {
defer Output.flush();
var events = try INotify.read();
@@ -417,7 +448,21 @@ pub fn NewWatcher(comptime ContextType: type) type {
watch_event_id += 1;
}
- this.ctx.onFileUpdate(watchevents[0..watch_event_id], this.watchlist);
+ var all_events = watchevents[0..watch_event_id];
+ std.sort.sort(WatchEvent, all_events, void{}, WatchEvent.sortByIndex);
+
+ var last_event_index: usize = 0;
+ var last_event_id: INotify.EventListIndex = std.math.maxInt(INotify.EventListIndex);
+ for (all_events) |event, i| {
+ if (event.index == last_event_id) {
+ all_events[last_event_index].merge(event);
+ continue;
+ }
+ last_event_index = i;
+ last_event_id = event.index;
+ }
+ if (all_events.len == 0) continue :restart;
+ this.ctx.onFileUpdate(all_events[0..last_event_index+1], this.watchlist);
remaining_events -= slice.len;
}
}
@@ -503,11 +548,13 @@ pub fn NewWatcher(comptime ContextType: type) type {
null,
);
} else if (Environment.isLinux) {
- var sentineled = file_path_;
- var file_path_to_use_ptr: [*c]u8 = @intToPtr([*c]u8, @ptrToInt(file_path_.ptr));
- var file_path_to_use: [:0]u8 = file_path_to_use_ptr[0..sentineled.len :0];
-
- index = try INotify.watchPath(file_path_to_use);
+ // var file_path_to_use_ = std.mem.trimRight(u8, file_path_, "/");
+ // var buf: [std.fs.MAX_PATH_BYTES+1]u8 = undefined;
+ // std.mem.copy(u8, &buf, file_path_to_use_);
+ // buf[file_path_to_use_.len] = 0;
+ var buf = file_path_.ptr;
+ var slice: [:0]const u8 = buf[0..file_path_.len:0];
+ index = try INotify.watchPath(slice);
}
this.watchlist.appendAssumeCapacity(.{
@@ -587,12 +634,12 @@ pub fn NewWatcher(comptime ContextType: type) type {
null,
);
} else if (Environment.isLinux) {
- // This works around a Zig compiler bug when casting a slice from a string to a sentineled string.
- var sentineled = file_path_;
- var file_path_to_use_ptr: [*c]u8 = @intToPtr([*c]u8, @ptrToInt(file_path_.ptr));
- var file_path_to_use: [:0]u8 = file_path_to_use_ptr[0..sentineled.len :0];
-
- index = try INotify.watchPath(file_path_to_use);
+ var file_path_to_use_ = std.mem.trimRight(u8, file_path_, "/");
+ var buf: [std.fs.MAX_PATH_BYTES+1]u8 = undefined;
+ std.mem.copy(u8, &buf, file_path_to_use_);
+ buf[file_path_to_use_.len] = 0;
+ var slice: [:0]u8 = buf[0..file_path_to_use_.len:0];
+ index = try INotify.watchDir(slice);
}
this.watchlist.appendAssumeCapacity(.{