mirror of
https://github.com/oven-sh/bun
synced 2026-02-11 11:29:02 +00:00
Implement Workers (#3645)
* copy files * format * options * Introduce `Worker`, `onmessage`, `onerror`, and `postMessage` globals * Stub `Worker.prototype.ref` & `Worker.prototype.unref` * Update web_worker.zig * Worker works * Add "mini" mode * add wakeup * Partially fix the keep-alive issue * clean up refer behavior * Implement `serialize` & `deserialize` in `bun:jsc` & add polyfill for `node:v8` * Types & docs * Update globals.d.ts * Add mutex * Fixes --------- Co-authored-by: Dylan Conway <dylan.conway567@gmail.com> Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
This commit is contained in:
@@ -96,7 +96,7 @@ pub const GlobalConstructors = [_]type{
|
||||
pub const GlobalClasses = [_]type{
|
||||
Bun.Class,
|
||||
WebCore.Crypto.Class,
|
||||
EventListenerMixin.addEventListener(VirtualMachine),
|
||||
// EventListenerMixin.addEventListener(VirtualMachine),
|
||||
|
||||
// Fetch.Class,
|
||||
js_ast.Macro.JSNode.BunJSXCallbackFunction,
|
||||
@@ -353,7 +353,8 @@ pub const ExitHandler = struct {
|
||||
JSC.markBinding(@src());
|
||||
var vm = @fieldParentPtr(VirtualMachine, "exit_handler", this);
|
||||
Process__dispatchOnExit(vm.global, this.exit_code);
|
||||
Bun__closeAllSQLiteDatabasesForTermination();
|
||||
if (vm.isMainThread())
|
||||
Bun__closeAllSQLiteDatabasesForTermination();
|
||||
}
|
||||
|
||||
pub fn dispatchOnBeforeExit(this: *ExitHandler) void {
|
||||
@@ -363,6 +364,8 @@ pub const ExitHandler = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const WebWorker = @import("./web_worker.zig").WebWorker;
|
||||
|
||||
/// TODO: rename this to ScriptExecutionContext
|
||||
/// This is the shared global state for a single JS instance execution
|
||||
/// Today, Bun is one VM per thread, so the name "VirtualMachine" sort of makes sense
|
||||
@@ -482,11 +485,16 @@ pub const VirtualMachine = struct {
|
||||
parser_arena: ?@import("root").bun.ArenaAllocator = null,
|
||||
|
||||
gc_controller: JSC.GarbageCollectionController = .{},
|
||||
worker: ?*JSC.WebWorker = null,
|
||||
|
||||
pub const OnUnhandledRejection = fn (*VirtualMachine, globalObject: *JSC.JSGlobalObject, JSC.JSValue) void;
|
||||
|
||||
pub const OnException = fn (*ZigException) void;
|
||||
|
||||
pub fn isMainThread(this: *const VirtualMachine) bool {
|
||||
return this.worker == null;
|
||||
}
|
||||
|
||||
pub fn setOnException(this: *VirtualMachine, callback: *const OnException) void {
|
||||
this.on_exception = callback;
|
||||
}
|
||||
@@ -877,6 +885,8 @@ pub const VirtualMachine = struct {
|
||||
&global_classes,
|
||||
@intCast(i32, global_classes.len),
|
||||
vm.console,
|
||||
-1,
|
||||
false,
|
||||
);
|
||||
vm.regular_event_loop.global = vm.global;
|
||||
vm.regular_event_loop.virtual_machine = vm;
|
||||
@@ -976,6 +986,110 @@ pub const VirtualMachine = struct {
|
||||
&global_classes,
|
||||
@intCast(i32, global_classes.len),
|
||||
vm.console,
|
||||
-1,
|
||||
false,
|
||||
);
|
||||
vm.regular_event_loop.global = vm.global;
|
||||
vm.regular_event_loop.virtual_machine = vm;
|
||||
|
||||
if (source_code_printer == null) {
|
||||
var writer = try js_printer.BufferWriter.init(allocator);
|
||||
source_code_printer = allocator.create(js_printer.BufferPrinter) catch unreachable;
|
||||
source_code_printer.?.* = js_printer.BufferPrinter.init(writer);
|
||||
source_code_printer.?.ctx.append_null_byte = false;
|
||||
}
|
||||
|
||||
return vm;
|
||||
}
|
||||
|
||||
pub fn initWorker(
|
||||
allocator: std.mem.Allocator,
|
||||
_args: Api.TransformOptions,
|
||||
_log: ?*logger.Log,
|
||||
env_loader: ?*DotEnv.Loader,
|
||||
store_fd: bool,
|
||||
worker: *WebWorker,
|
||||
) anyerror!*VirtualMachine {
|
||||
var log: *logger.Log = undefined;
|
||||
if (_log) |__log| {
|
||||
log = __log;
|
||||
} else {
|
||||
log = try allocator.create(logger.Log);
|
||||
log.* = logger.Log.init(allocator);
|
||||
}
|
||||
|
||||
VMHolder.vm = try allocator.create(VirtualMachine);
|
||||
var console = try allocator.create(ZigConsoleClient);
|
||||
console.* = ZigConsoleClient.init(Output.errorWriter(), Output.writer());
|
||||
const bundler = try Bundler.init(
|
||||
allocator,
|
||||
log,
|
||||
try Config.configureTransformOptionsForBunVM(allocator, _args),
|
||||
null,
|
||||
env_loader,
|
||||
);
|
||||
var vm = VMHolder.vm.?;
|
||||
|
||||
vm.* = VirtualMachine{
|
||||
.global = undefined,
|
||||
.allocator = allocator,
|
||||
.entry_point = ServerEntryPoint{},
|
||||
.event_listeners = EventListenerMixin.Map.init(allocator),
|
||||
.bundler = bundler,
|
||||
.console = console,
|
||||
.node_modules = bundler.options.node_modules_bundle,
|
||||
.log = log,
|
||||
.flush_list = std.ArrayList(string).init(allocator),
|
||||
.blobs = if (_args.serve orelse false) try Blob.Group.init(allocator) else null,
|
||||
.origin = bundler.options.origin,
|
||||
.saved_source_map_table = SavedSourceMap.HashTable.init(allocator),
|
||||
.source_mappings = undefined,
|
||||
.macros = MacroMap.init(allocator),
|
||||
.macro_entry_points = @TypeOf(vm.macro_entry_points).init(allocator),
|
||||
.origin_timer = std.time.Timer.start() catch @panic("Please don't mess with timers."),
|
||||
.origin_timestamp = getOriginTimestamp(),
|
||||
.ref_strings = JSC.RefString.Map.init(allocator),
|
||||
.file_blobs = JSC.WebCore.Blob.Store.Map.init(allocator),
|
||||
.parser_arena = @import("root").bun.ArenaAllocator.init(allocator),
|
||||
.standalone_module_graph = worker.parent.standalone_module_graph,
|
||||
.worker = worker,
|
||||
};
|
||||
vm.source_mappings = .{ .map = &vm.saved_source_map_table };
|
||||
vm.regular_event_loop.tasks = EventLoop.Queue.init(
|
||||
default_allocator,
|
||||
);
|
||||
vm.regular_event_loop.tasks.ensureUnusedCapacity(64) catch unreachable;
|
||||
vm.regular_event_loop.concurrent_tasks = .{};
|
||||
vm.event_loop = &vm.regular_event_loop;
|
||||
vm.hot_reload = worker.parent.hot_reload;
|
||||
vm.bundler.macro_context = null;
|
||||
vm.bundler.resolver.store_fd = store_fd;
|
||||
vm.bundler.resolver.prefer_module_field = false;
|
||||
vm.bundler.resolver.onWakePackageManager = .{
|
||||
.context = &vm.modules,
|
||||
.handler = ModuleLoader.AsyncModule.Queue.onWakeHandler,
|
||||
.onDependencyError = JSC.ModuleLoader.AsyncModule.Queue.onDependencyError,
|
||||
};
|
||||
|
||||
vm.bundler.configureLinker();
|
||||
try vm.bundler.configureFramework(false);
|
||||
|
||||
vm.bundler.macro_context = js_ast.Macro.MacroContext.init(&vm.bundler);
|
||||
|
||||
if (_args.serve orelse false) {
|
||||
vm.bundler.linker.onImportCSS = Bun.onImportCSS;
|
||||
}
|
||||
|
||||
var global_classes: [GlobalClasses.len]js.JSClassRef = undefined;
|
||||
inline for (GlobalClasses, 0..) |Class, i| {
|
||||
global_classes[i] = Class.get().*;
|
||||
}
|
||||
vm.global = ZigGlobalObject.create(
|
||||
&global_classes,
|
||||
@intCast(i32, global_classes.len),
|
||||
vm.console,
|
||||
@intCast(i32, worker.execution_context_id),
|
||||
worker.mini,
|
||||
);
|
||||
vm.regular_event_loop.global = vm.global;
|
||||
vm.regular_event_loop.virtual_machine = vm;
|
||||
@@ -1753,7 +1867,7 @@ pub const VirtualMachine = struct {
|
||||
return promise;
|
||||
}
|
||||
|
||||
pub fn loadEntryPoint(this: *VirtualMachine, entry_path: string) !*JSInternalPromise {
|
||||
pub fn loadEntryPoint(this: *VirtualMachine, entry_path: string) anyerror!*JSInternalPromise {
|
||||
var promise = try this.reloadEntryPoint(entry_path);
|
||||
|
||||
// pending_internal_promise can change if hot module reloading is enabled
|
||||
|
||||
Reference in New Issue
Block a user