diff options
Diffstat (limited to 'src/bun.js')
m--------- | src/bun.js/WebKit | 0 | ||||
-rw-r--r-- | src/bun.js/bindings/BunClientData.cpp | 8 | ||||
-rw-r--r-- | src/bun.js/bindings/BunClientData.h | 6 | ||||
-rw-r--r-- | src/bun.js/bindings/JSCTaskScheduler.cpp | 101 | ||||
-rw-r--r-- | src/bun.js/bindings/JSCTaskScheduler.h | 25 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 6 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 10 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/headers.zig | 1 | ||||
-rw-r--r-- | src/bun.js/event_loop.zig | 52 | ||||
-rw-r--r-- | src/bun.js/web_worker.zig | 4 | ||||
-rw-r--r-- | src/bun.js/webcore/response.zig | 2 | ||||
-rw-r--r-- | src/bun.js/webcore/streams.zig | 2 |
14 files changed, 189 insertions, 31 deletions
diff --git a/src/bun.js/WebKit b/src/bun.js/WebKit -Subproject 5e65b9ae7756b6353cfeac5206f7076b00e9ecf +Subproject ecf1b9c4bc2247020add7b14a732bb0085f25d5 diff --git a/src/bun.js/bindings/BunClientData.cpp b/src/bun.js/bindings/BunClientData.cpp index 2cdd8b25a..9af17d638 100644 --- a/src/bun.js/bindings/BunClientData.cpp +++ b/src/bun.js/bindings/BunClientData.cpp @@ -21,6 +21,7 @@ #include "JavaScriptCore/JSCInlines.h" #include "JSDOMWrapper.h" +#include <JavaScriptCore/DeferredWorkTimer.h> namespace WebCore { using namespace JSC; @@ -69,9 +70,14 @@ JSVMClientData::~JSVMClientData() ASSERT(m_normalWorld->hasOneRef()); m_normalWorld = nullptr; } -void JSVMClientData::create(VM* vm) +void JSVMClientData::create(VM* vm, void* bunVM) { JSVMClientData* clientData = new JSVMClientData(*vm); + clientData->bunVM = bunVM; + vm->deferredWorkTimer->onAddPendingWork = Bun::JSCTaskScheduler::onAddPendingWork; + vm->deferredWorkTimer->onScheduleWorkSoon = Bun::JSCTaskScheduler::onScheduleWorkSoon; + vm->deferredWorkTimer->onCancelPendingWork = Bun::JSCTaskScheduler::onCancelPendingWork; + vm->clientData = clientData; // ~VM deletes this pointer. clientData->m_normalWorld = DOMWrapperWorld::create(*vm, DOMWrapperWorld::Type::Normal); diff --git a/src/bun.js/bindings/BunClientData.h b/src/bun.js/bindings/BunClientData.h index e3c2d3748..0bee575df 100644 --- a/src/bun.js/bindings/BunClientData.h +++ b/src/bun.js/bindings/BunClientData.h @@ -24,6 +24,7 @@ class DOMWrapperWorld; #include "JavaScriptCore/IsoSubspacePerVM.h" #include "wtf/StdLibExtras.h" #include "WebCoreJSBuiltins.h" +#include "JSCTaskScheduler.h" namespace Zig { } @@ -80,7 +81,7 @@ public: virtual ~JSVMClientData(); - static void create(JSC::VM*); + static void create(JSC::VM*, void*); JSHeapData& heapData() { return *m_heapData; } BunBuiltinNames& builtinNames() { return m_builtinNames; } @@ -107,6 +108,9 @@ public: func(*space); } + void* bunVM; + Bun::JSCTaskScheduler deferredWorkTimer; + private: BunBuiltinNames m_builtinNames; JSBuiltinFunctions m_builtinFunctions; diff --git a/src/bun.js/bindings/JSCTaskScheduler.cpp b/src/bun.js/bindings/JSCTaskScheduler.cpp new file mode 100644 index 000000000..436be4c0a --- /dev/null +++ b/src/bun.js/bindings/JSCTaskScheduler.cpp @@ -0,0 +1,101 @@ +#include "config.h" +#include <JavaScriptCore/VM.h> +#include "JSCTaskScheduler.h" +#include "BunClientData.h" + +using Ticket = JSC::DeferredWorkTimer::Ticket; +using Task = JSC::DeferredWorkTimer::Task; +using TicketData = JSC::DeferredWorkTimer::TicketData; + +namespace Bun { +using namespace JSC; + +extern "C" void Bun__queueJSCDeferredWorkTaskConcurrently(void* bunVM, void* task); +extern "C" void Bun__eventLoop__incrementRefConcurrently(void* bunVM, int delta); + +class JSCDeferredWorkTask { +public: + JSCDeferredWorkTask(Ticket ticket, Task&& task) + : ticket(ticket) + , task(WTFMove(task)) + { + } + + Ticket ticket; + Task task; + + WTF_MAKE_FAST_ALLOCATED; +}; + +static JSC::VM& getVM(Ticket ticket) +{ + return ticket->scriptExecutionOwner.get()->vm(); +} + +void JSCTaskScheduler::onAddPendingWork(std::unique_ptr<TicketData> ticket, JSC::DeferredWorkTimer::WorkKind kind) +{ + JSC::VM& vm = getVM(ticket.get()); + auto clientData = WebCore::clientData(vm); + auto& scheduler = clientData->deferredWorkTimer; + LockHolder holder { scheduler.m_lock }; + if (kind != DeferredWorkTimer::WorkKind::Other) { + + Bun__eventLoop__incrementRefConcurrently(clientData->bunVM, 1); + scheduler.m_pendingTicketsKeepingEventLoopAlive.add(WTFMove(ticket)); + } else { + scheduler.m_pendingTicketsOther.add(WTFMove(ticket)); + } +} +void JSCTaskScheduler::onScheduleWorkSoon(Ticket ticket, Task&& task) +{ + auto* job = new JSCDeferredWorkTask(ticket, WTFMove(task)); + Bun__queueJSCDeferredWorkTaskConcurrently(WebCore::clientData(getVM(ticket))->bunVM, job); +} + +void JSCTaskScheduler::onCancelPendingWork(Ticket ticket) +{ + auto& scheduler = WebCore::clientData(getVM(ticket))->deferredWorkTimer; + + LockHolder holder { scheduler.m_lock }; + bool isKeepingEventLoopAlive = scheduler.m_pendingTicketsKeepingEventLoopAlive.removeIf([ticket](const auto& pendingTicket) { + return pendingTicket.get() == ticket; + }); + + if (isKeepingEventLoopAlive) { + holder.unlockEarly(); + JSC::VM& vm = getVM(ticket); + Bun__eventLoop__incrementRefConcurrently(WebCore::clientData(vm)->bunVM, -1); + } else { + scheduler.m_pendingTicketsOther.removeIf([ticket](const auto& pendingTicket) { + return pendingTicket.get() == ticket; + }); + } +} + +static void runPendingWork(void* bunVM, Bun::JSCTaskScheduler& scheduler, JSCDeferredWorkTask* job) +{ + LockHolder holder { scheduler.m_lock }; + auto pendingTicket = scheduler.m_pendingTicketsKeepingEventLoopAlive.take(job->ticket); + if (!pendingTicket) { + pendingTicket = scheduler.m_pendingTicketsOther.take(job->ticket); + } else { + Bun__eventLoop__incrementRefConcurrently(bunVM, -1); + } + holder.unlockEarly(); + + if (pendingTicket && !pendingTicket->isCancelled()) { + job->task(job->ticket); + } + + delete job; +} + +extern "C" void Bun__runDeferredWork(Bun::JSCDeferredWorkTask* job) +{ + auto& vm = getVM(job->ticket); + auto clientData = WebCore::clientData(vm); + + runPendingWork(clientData->bunVM, clientData->deferredWorkTimer, job); +} + +} diff --git a/src/bun.js/bindings/JSCTaskScheduler.h b/src/bun.js/bindings/JSCTaskScheduler.h new file mode 100644 index 000000000..3257eb9c8 --- /dev/null +++ b/src/bun.js/bindings/JSCTaskScheduler.h @@ -0,0 +1,25 @@ +#pragma once + +#include <JavaScriptCore/DeferredWorkTimer.h> + +namespace Bun { + +class JSCTaskScheduler { +public: + JSCTaskScheduler() + : m_pendingTicketsKeepingEventLoopAlive() + , m_pendingTicketsOther() + { + } + + static void onAddPendingWork(std::unique_ptr<JSC::DeferredWorkTimer::TicketData> ticket, JSC::DeferredWorkTimer::WorkKind kind); + static void onScheduleWorkSoon(JSC::DeferredWorkTimer::Ticket ticket, JSC::DeferredWorkTimer::Task&& task); + static void onCancelPendingWork(JSC::DeferredWorkTimer::Ticket ticket); + +public: + Lock m_lock; + HashSet<std::unique_ptr<JSC::DeferredWorkTimer::TicketData>> m_pendingTicketsKeepingEventLoopAlive; + HashSet<std::unique_ptr<JSC::DeferredWorkTimer::TicketData>> m_pendingTicketsOther; +}; + +}
\ No newline at end of file diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index f267fcbb7..52f633f3d 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -431,7 +431,7 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(JSClassRef* globalObje // This must happen before JSVMClientData::create vm.heap.acquireAccess(); - WebCore::JSVMClientData::create(&vm); + WebCore::JSVMClientData::create(&vm, Bun__getVM()); JSC::JSLockHolder locker(vm); Zig::GlobalObject* globalObject; diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 55f718207..9fb0c18d5 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -9,7 +9,6 @@ #include "JavaScriptCore/BytecodeIndex.h" #include "JavaScriptCore/CodeBlock.h" #include "JavaScriptCore/Completion.h" -#include "JavaScriptCore/DeferredWorkTimer.h" #include "JavaScriptCore/ErrorInstance.h" #include "JavaScriptCore/ExceptionHelpers.h" #include "JavaScriptCore/ExceptionScope.h" @@ -3893,11 +3892,6 @@ void JSC__VM__deleteAllCode(JSC__VM* arg1, JSC__JSGlobalObject* globalObject) arg1->heap.reportAbandonedObjectGraph(); } -void JSC__VM__doWork(JSC__VM* vm) -{ - vm->deferredWorkTimer->doWork(*vm); -} - void JSC__VM__deinit(JSC__VM* arg1, JSC__JSGlobalObject* globalObject) {} void JSC__VM__drainMicrotasks(JSC__VM* arg0) { arg0->drainMicrotasks(); } diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index edd1be1a9..924eb27a1 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -5083,14 +5083,6 @@ pub const VM = extern struct { }); } - pub fn doWork( - vm: *VM, - ) void { - return cppFn("doWork", .{ - vm, - }); - } - pub fn externalMemorySize(vm: *VM) usize { return cppFn("externalMemorySize", .{vm}); } @@ -5101,7 +5093,7 @@ pub const VM = extern struct { return cppFn("blockBytesAllocated", .{vm}); } - pub const Extern = [_][]const u8{ "collectAsync", "externalMemorySize", "blockBytesAllocated", "heapSize", "releaseWeakRefs", "throwError", "doWork", "deferGC", "holdAPILock", "runGC", "generateHeapSnapshot", "isJITEnabled", "deleteAllCode", "create", "deinit", "setExecutionForbidden", "executionForbidden", "isEntered", "throwError", "drainMicrotasks", "whenIdle", "shrinkFootprint", "setExecutionTimeLimit", "clearExecutionTimeLimit" }; + pub const Extern = [_][]const u8{ "collectAsync", "externalMemorySize", "blockBytesAllocated", "heapSize", "releaseWeakRefs", "throwError", "deferGC", "holdAPILock", "runGC", "generateHeapSnapshot", "isJITEnabled", "deleteAllCode", "create", "deinit", "setExecutionForbidden", "executionForbidden", "isEntered", "throwError", "drainMicrotasks", "whenIdle", "shrinkFootprint", "setExecutionTimeLimit", "clearExecutionTimeLimit" }; }; pub const ThrowScope = extern struct { diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 8cce986dc..05c708a48 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -419,7 +419,6 @@ CPP_DECL JSC__VM* JSC__VM__create(unsigned char HeapType0); CPP_DECL void JSC__VM__deferGC(JSC__VM* arg0, void* arg1, void(* ArgFn2)(void* arg0)) __attribute__((nonnull (2))); CPP_DECL void JSC__VM__deinit(JSC__VM* arg0, JSC__JSGlobalObject* arg1); CPP_DECL void JSC__VM__deleteAllCode(JSC__VM* arg0, JSC__JSGlobalObject* arg1); -CPP_DECL void JSC__VM__doWork(JSC__VM* arg0); CPP_DECL void JSC__VM__drainMicrotasks(JSC__VM* arg0); CPP_DECL bool JSC__VM__executionForbidden(JSC__VM* arg0); CPP_DECL size_t JSC__VM__externalMemorySize(JSC__VM* arg0); diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index e87e59946..fbca33a30 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -313,7 +313,6 @@ pub extern fn JSC__VM__create(HeapType0: u8) *bindings.VM; pub extern fn JSC__VM__deferGC(arg0: *bindings.VM, arg1: ?*anyopaque, ArgFn2: ?*const fn (?*anyopaque) callconv(.C) void) void; pub extern fn JSC__VM__deinit(arg0: *bindings.VM, arg1: *bindings.JSGlobalObject) void; pub extern fn JSC__VM__deleteAllCode(arg0: *bindings.VM, arg1: *bindings.JSGlobalObject) void; -pub extern fn JSC__VM__doWork(arg0: *bindings.VM) void; pub extern fn JSC__VM__drainMicrotasks(arg0: *bindings.VM) void; pub extern fn JSC__VM__executionForbidden(arg0: *bindings.VM) bool; pub extern fn JSC__VM__externalMemorySize(arg0: *bindings.VM) usize; diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index 2dbe88d47..6688eaeea 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -73,7 +73,7 @@ pub fn ConcurrentPromiseTask(comptime Context: type) type { } pub fn onFinish(this: *This) void { - this.event_loop.enqueueTaskConcurrent(this.concurrent_task.from(this)); + this.event_loop.enqueueTaskConcurrent(this.concurrent_task.from(this, .manual_deinit)); } pub fn deinit(this: *This) void { @@ -136,7 +136,7 @@ pub fn WorkTask(comptime Context: type, comptime async_io: bool) type { } pub fn onFinish(this: *This) void { - this.event_loop.enqueueTaskConcurrent(this.concurrent_task.from(this)); + this.event_loop.enqueueTaskConcurrent(this.concurrent_task.from(this, .manual_deinit)); } pub fn deinit(this: *This) void { @@ -221,6 +221,35 @@ pub const CppTask = opaque { Bun__performTask(global, this); } }; +pub const JSCScheduler = struct { + pub const JSCDeferredWorkTask = opaque { + extern fn Bun__runDeferredWork(task: *JSCScheduler.JSCDeferredWorkTask) void; + pub const run = Bun__runDeferredWork; + }; + + export fn Bun__eventLoop__incrementRefConcurrently(jsc_vm: *VirtualMachine, delta: c_int) void { + JSC.markBinding(@src()); + + if (delta > 0) { + jsc_vm.uws_event_loop.?.refConcurrently(); + } else { + jsc_vm.uws_event_loop.?.unrefConcurrently(); + } + } + + export fn Bun__queueJSCDeferredWorkTaskConcurrently(jsc_vm: *VirtualMachine, task: *JSCScheduler.JSCDeferredWorkTask) void { + JSC.markBinding(@src()); + var loop = jsc_vm.eventLoop(); + var concurrent_task = bun.default_allocator.create(ConcurrentTask) catch @panic("out of memory!"); + loop.enqueueTaskConcurrent(concurrent_task.from(task, .auto_deinit)); + } + + comptime { + _ = Bun__eventLoop__incrementRefConcurrently; + _ = Bun__queueJSCDeferredWorkTaskConcurrently; + } +}; + const ThreadSafeFunction = JSC.napi.ThreadSafeFunction; const MicrotaskForDefaultGlobalObject = JSC.MicrotaskForDefaultGlobalObject; const HotReloadTask = JSC.HotReloader.HotReloadTask; @@ -228,6 +257,7 @@ const FSWatchTask = JSC.Node.FSWatcher.FSWatchTask; const PollPendingModulesTask = JSC.ModuleLoader.AsyncModule.Queue; // const PromiseTask = JSInternalPromise.Completion.PromiseTask; const GetAddrInfoRequestTask = JSC.DNS.GetAddrInfoRequest.Task; +const JSCDeferredWorkTask = JSCScheduler.JSCDeferredWorkTask; pub const Task = TaggedPointerUnion(.{ FetchTasklet, Microtask, @@ -244,6 +274,8 @@ pub const Task = TaggedPointerUnion(.{ PollPendingModulesTask, GetAddrInfoRequestTask, FSWatchTask, + JSCDeferredWorkTask, + // PromiseTask, // TimeoutTasklet, }); @@ -255,10 +287,15 @@ pub const ConcurrentTask = struct { pub const Queue = UnboundedQueue(ConcurrentTask, .next); - pub fn from(this: *ConcurrentTask, of: anytype) *ConcurrentTask { + pub const AutoDeinit = enum { + manual_deinit, + auto_deinit, + }; + pub fn from(this: *ConcurrentTask, of: anytype, auto_deinit: AutoDeinit) *ConcurrentTask { this.* = .{ .task = Task.init(of), .next = null, + .auto_delete = auto_deinit == .auto_deinit, }; return this; } @@ -457,6 +494,11 @@ pub const EventLoop = struct { transform_task.*.runFromJS(); transform_task.deinit(); }, + @field(Task.Tag, bun.meta.typeBaseName(@typeName(JSCDeferredWorkTask))) => { + var jsc_task: *JSCDeferredWorkTask = task.get(JSCDeferredWorkTask).?; + JSC.markBinding(@src()); + jsc_task.run(); + }, @field(Task.Tag, @typeName(WriteFileTask)) => { var transform_task: *WriteFileTask = task.get(WriteFileTask).?; transform_task.*.runFromJS(); @@ -627,10 +669,6 @@ pub const EventLoop = struct { break; } - // TODO: unify the event loops - // This needs a hook into JSC to schedule timers - this.global.vm().doWork(); - while (this.tickWithCount() > 0) { this.tickConcurrent(); } diff --git a/src/bun.js/web_worker.zig b/src/bun.js/web_worker.zig index 504b0adeb..130e99be1 100644 --- a/src/bun.js/web_worker.zig +++ b/src/bun.js/web_worker.zig @@ -141,7 +141,7 @@ pub const WebWorker = struct { var any_task = bun.default_allocator.create(JSC.AnyTask) catch @panic("OOM"); any_task.* = AnyTask.init(this); var concurrent_task = bun.default_allocator.create(JSC.ConcurrentTask) catch @panic("OOM"); - this.parent.eventLoop().enqueueTaskConcurrent(concurrent_task.from(any_task)); + this.parent.eventLoop().enqueueTaskConcurrent(concurrent_task.from(any_task, .auto_deinit)); } pub fn startWithErrorHandling( @@ -413,7 +413,7 @@ pub const WebWorker = struct { var concurrent_task = bun.default_allocator.create(JSC.ConcurrentTask) catch @panic("OOM"); var task = bun.default_allocator.create(JSC.AnyTask) catch @panic("OOM"); task.* = JSC.AnyTask.New(WebWorker, onTerminate).init(this); - vm.eventLoop().enqueueTaskConcurrent(concurrent_task.from(task)); + vm.eventLoop().enqueueTaskConcurrent(concurrent_task.from(task, .auto_deinit)); return true; } }; diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index ab2a6010d..a70a4f0ee 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -979,7 +979,7 @@ pub const Fetch = struct { pub fn callback(task: *FetchTasklet, result: HTTPClient.HTTPClientResult) void { task.response_buffer = result.body.?.*; task.result = result; - task.javascript_vm.eventLoop().enqueueTaskConcurrent(task.concurrent_task.from(task)); + task.javascript_vm.eventLoop().enqueueTaskConcurrent(task.concurrent_task.from(task, .manual_deinit)); } }; diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index 03b1dcdc9..5dc4ea830 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -4168,7 +4168,7 @@ pub const File = struct { pub fn scheduleMainThreadTask(this: *File) void { this.concurrent.main_thread_task.ctx = this; - this.loop.enqueueTaskConcurrent(this.concurrent.concurrent_task.from(&this.concurrent.main_thread_task)); + this.loop.enqueueTaskConcurrent(this.concurrent.concurrent_task.from(&this.concurrent.main_thread_task, .manual_deinit)); } fn runAsync(this: *File) void { |