diff options
author | 2023-09-05 16:52:57 -0800 | |
---|---|---|
committer | 2023-09-05 17:52:57 -0700 | |
commit | 1bd5b245b8a55353e60a2decad507ef8014be044 (patch) | |
tree | 1a5cd5bcc7d7758bbfd154cf49470c1b0f3dc1bb /src/bun.js | |
parent | acfd028e8f859a0e8139b7adab5d319e326c2373 (diff) | |
download | bun-1bd5b245b8a55353e60a2decad507ef8014be044.tar.gz bun-1bd5b245b8a55353e60a2decad507ef8014be044.tar.zst bun-1bd5b245b8a55353e60a2decad507ef8014be044.zip |
Align `process.nextTick` execution order with Node (#4409)
* Align `process.nextTick` execution order with Node
* some tests
* formatting
* fixups
* fix the test failures
* simplify the logic here
* push it up
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Co-authored-by: dave caruso <me@paperdave.net>
Diffstat (limited to 'src/bun.js')
-rw-r--r-- | src/bun.js/bindings/JSNextTickQueue.cpp | 97 | ||||
-rw-r--r-- | src/bun.js/bindings/JSNextTickQueue.h | 41 | ||||
-rw-r--r-- | src/bun.js/bindings/Process.cpp | 98 | ||||
-rw-r--r-- | src/bun.js/bindings/Process.lut.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 124 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.h | 5 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.cpp | 3 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/webcore/DOMIsoSubspaces.h | 2 | ||||
-rw-r--r-- | src/bun.js/event_loop.zig | 15 |
10 files changed, 309 insertions, 79 deletions
diff --git a/src/bun.js/bindings/JSNextTickQueue.cpp b/src/bun.js/bindings/JSNextTickQueue.cpp new file mode 100644 index 000000000..8916ef6c8 --- /dev/null +++ b/src/bun.js/bindings/JSNextTickQueue.cpp @@ -0,0 +1,97 @@ +#include "root.h" + +#include "JavaScriptCore/JSCJSValueInlines.h" +#include "JavaScriptCore/JSInternalPromise.h" +#include "JavaScriptCore/LazyPropertyInlines.h" +#include <JavaScriptCore/Weak.h> +#include <JavaScriptCore/GetterSetter.h> + +#include "JSNextTickQueue.h" +#include <JavaScriptCore/JSGlobalObject.h> +#include <JavaScriptCore/Structure.h> +#include <JavaScriptCore/JSInternalFieldObjectImplInlines.h> +#include "ExtendedDOMClientIsoSubspaces.h" +#include "ExtendedDOMIsoSubspaces.h" +#include "BunClientData.h" + +namespace Bun { + +using namespace JSC; + +const JSC::ClassInfo JSNextTickQueue::s_info = { "JSNextTickQueue"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSNextTickQueue) }; + +template<typename, JSC::SubspaceAccess mode> +JSC::GCClient::IsoSubspace* JSNextTickQueue::subspaceFor(JSC::VM& vm) +{ + return WebCore::subspaceForImpl<JSNextTickQueue, WebCore::UseCustomHeapCellType::No>( + vm, + [](auto& spaces) { return spaces.m_clientSubspaceForJSNextTickQueue.get(); }, + [](auto& spaces, auto&& space) { spaces.m_clientSubspaceForJSNextTickQueue = std::forward<decltype(space)>(space); }, + [](auto& spaces) { return spaces.m_subspaceForJSNextTickQueue.get(); }, + [](auto& spaces, auto&& space) { spaces.m_subspaceForJSNextTickQueue = std::forward<decltype(space)>(space); }); +} + +JSNextTickQueue* JSNextTickQueue::create(VM& vm, Structure* structure) +{ + JSNextTickQueue* mod = new (NotNull, allocateCell<JSNextTickQueue>(vm)) JSNextTickQueue(vm, structure); + return mod; +} +Structure* JSNextTickQueue::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); +} + +JSNextTickQueue::JSNextTickQueue(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +void JSNextTickQueue::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +template<typename Visitor> +void JSNextTickQueue::visitChildrenImpl(JSCell* cell, Visitor& visitor) +{ + auto* thisObject = jsCast<JSNextTickQueue*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); +} + +DEFINE_VISIT_CHILDREN(JSNextTickQueue); + +JSNextTickQueue* JSNextTickQueue::create(JSC::JSGlobalObject* globalObject) +{ + auto& vm = globalObject->vm(); + auto* obj = create(vm, createStructure(vm, globalObject, jsNull())); + obj->finishCreation(vm); + return obj; +} + +bool JSNextTickQueue::isEmpty() +{ + return !internalField(0) || internalField(0).get().asNumber() == 0; +} + +void JSNextTickQueue::drain(JSC::VM& vm, JSC::JSGlobalObject* globalObject) +{ + bool mustResetContext = false; + if (isEmpty()) { + vm.drainMicrotasks(); + mustResetContext = true; + } + + if (!isEmpty()) { + if (mustResetContext) { + globalObject->m_asyncContextData.get()->putInternalField(vm, 0, jsUndefined()); + } + auto* drainFn = internalField(2).get().getObject(); + + auto throwScope = DECLARE_THROW_SCOPE(vm); + MarkedArgumentBuffer drainArgs; + JSC::call(globalObject, drainFn, drainArgs, "Failed to drain next tick queue"_s); + } +} + +}
\ No newline at end of file diff --git a/src/bun.js/bindings/JSNextTickQueue.h b/src/bun.js/bindings/JSNextTickQueue.h new file mode 100644 index 000000000..c3bd228cc --- /dev/null +++ b/src/bun.js/bindings/JSNextTickQueue.h @@ -0,0 +1,41 @@ +#include "root.h" +#include "headers-handwritten.h" + +#include "JavaScriptCore/JSCInlines.h" +#include "BunClientData.h" +#include <JavaScriptCore/JSInternalFieldObjectImpl.h> + +namespace Bun { +using namespace JSC; + +class JSNextTickQueue : public JSC::JSInternalFieldObjectImpl<3> { +public: + static constexpr unsigned numberOfInternalFields = 3; + using Base = JSC::JSInternalFieldObjectImpl<3>; + + template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm); + + JS_EXPORT_PRIVATE static JSNextTickQueue* create(VM&, Structure*); + static JSNextTickQueue* create(JSC::JSGlobalObject* globalObject); + static JSNextTickQueue* createWithInitialValues(VM&, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + static std::array<JSValue, numberOfInternalFields> initialValues() + { + return { { + jsNumber(-1), + jsUndefined(), + jsUndefined(), + } }; + } + + DECLARE_EXPORT_INFO; + DECLARE_VISIT_CHILDREN; + + JSNextTickQueue(JSC::VM&, JSC::Structure*); + void finishCreation(JSC::VM&); + + bool isEmpty(); + void drain(JSC::VM& vm, JSC::JSGlobalObject* globalObject); +}; +}
\ No newline at end of file diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 5c9c03dd2..252d00075 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -19,6 +19,7 @@ #include <termios.h> #include <errno.h> #include <sys/ioctl.h> +#include "JSNextTickQueue.h" #pragma mark - Node.js Process @@ -160,63 +161,6 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionInternalGetWindowSize, return JSC::JSValue::encode(jsBoolean(true)); } -JSC_DEFINE_HOST_FUNCTION(Process_functionNextTick, - (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) -{ - JSC::VM& vm = globalObject->vm(); - auto argCount = callFrame->argumentCount(); - if (argCount == 0) { - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - JSC::throwTypeError(globalObject, scope, "nextTick requires 1 argument (a function)"_s); - return JSC::JSValue::encode(JSC::JSValue {}); - } - - JSC::JSValue job = callFrame->uncheckedArgument(0); - - if (!job.isObject() || !job.getObject()->isCallable()) { - auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); - JSC::throwTypeError(globalObject, scope, "nextTick expects a function"_s); - return JSC::JSValue::encode(JSC::JSValue {}); - } - - Zig::GlobalObject* global = JSC::jsCast<Zig::GlobalObject*>(globalObject); - JSC::JSValue asyncContextValue = globalObject->m_asyncContextData.get()->getInternalField(0); - - switch (callFrame->argumentCount()) { - case 1: { - global->queueMicrotask(global->performMicrotaskFunction(), job, asyncContextValue, JSC::JSValue {}, JSC::JSValue {}); - break; - } - case 2: { - global->queueMicrotask(global->performMicrotaskFunction(), job, asyncContextValue, callFrame->uncheckedArgument(1), JSC::JSValue {}); - break; - } - case 3: { - global->queueMicrotask(global->performMicrotaskFunction(), job, asyncContextValue, callFrame->uncheckedArgument(1), callFrame->uncheckedArgument(2)); - break; - } - default: { - JSC::JSArray* args = JSC::constructEmptyArray(globalObject, nullptr, argCount - 1); - if (UNLIKELY(!args)) { - auto scope = DECLARE_THROW_SCOPE(vm); - throwVMError(globalObject, scope, createOutOfMemoryError(globalObject)); - return JSC::JSValue::encode(JSC::JSValue {}); - } - - for (unsigned i = 1; i < argCount; i++) { - args->putDirectIndex(globalObject, i - 1, callFrame->uncheckedArgument(i)); - } - - global->queueMicrotask( - global->performMicrotaskVariadicFunction(), job, args, asyncContextValue, JSC::JSValue {}); - - break; - } - } - - return JSC::JSValue::encode(jsUndefined()); -} - JSC_DECLARE_HOST_FUNCTION(Process_functionDlopen); JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalObject_, JSC::CallFrame* callFrame)) @@ -279,7 +223,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, } } - JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject* globalObject, + JSC::EncodedJSValue (*napi_register_module_v1)(JSC::JSGlobalObject * globalObject, JSC::EncodedJSValue exports); napi_register_module_v1 = reinterpret_cast<JSC::EncodedJSValue (*)(JSC::JSGlobalObject*, @@ -1533,6 +1477,42 @@ static JSValue constructMemoryUsage(VM& vm, JSObject* processObject) return memoryUsage; } +JSC_DEFINE_HOST_FUNCTION(jsFunctionReportUncaughtException, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + JSValue arg0 = callFrame->argument(0); + Bun__reportUnhandledError(globalObject, JSValue::encode(arg0)); + return JSValue::encode(jsUndefined()); +} + +JSC_DEFINE_HOST_FUNCTION(jsFunctionDrainMicrotaskQueue, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) +{ + globalObject->vm().drainMicrotasks(); + return JSValue::encode(jsUndefined()); +} + +static JSValue constructProcessNextTickFn(VM& vm, JSObject* processObject) +{ + JSGlobalObject* lexicalGlobalObject = processObject->globalObject(); + Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject); + JSValue nextTickQueueObject; + if (!globalObject->m_nextTickQueue) { + Bun::JSNextTickQueue* queue = Bun::JSNextTickQueue::create(globalObject); + globalObject->m_nextTickQueue.set(vm, globalObject, queue); + nextTickQueueObject = queue; + } else { + nextTickQueueObject = jsCast<Bun::JSNextTickQueue*>(globalObject->m_nextTickQueue.get()); + } + + JSC::JSFunction* initializer = JSC::JSFunction::create(vm, processObjectInternalsInitializeNextTickQueueCodeGenerator(vm), lexicalGlobalObject); + JSC::MarkedArgumentBuffer args; + args.append(processObject); + args.append(nextTickQueueObject); + args.append(JSC::JSFunction::create(vm, globalObject, 1, String(), jsFunctionDrainMicrotaskQueue, ImplementationVisibility::Private)); + args.append(JSC::JSFunction::create(vm, globalObject, 1, String(), jsFunctionReportUncaughtException, ImplementationVisibility::Private)); + + return JSC::call(globalObject, initializer, JSC::getCallData(initializer), globalObject->globalThis(), args); +} + static JSValue constructFeatures(VM& vm, JSObject* processObject) { // { @@ -1742,7 +1722,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionKill, mainModule JSBuiltin ReadOnly|Builtin|Accessor|Function 0 memoryUsage constructMemoryUsage PropertyCallback moduleLoadList Process_stubEmptyArray PropertyCallback - nextTick Process_functionNextTick Function 1 + nextTick constructProcessNextTickFn PropertyCallback openStdin Process_functionOpenStdin Function 0 pid constructPid PropertyCallback platform constructPlatform PropertyCallback diff --git a/src/bun.js/bindings/Process.lut.h b/src/bun.js/bindings/Process.lut.h index 3f2d9255d..4086fb19e 100644 --- a/src/bun.js/bindings/Process.lut.h +++ b/src/bun.js/bindings/Process.lut.h @@ -179,7 +179,7 @@ static const struct HashTableValue processObjectTableValues[62] = { { "mainModule"_s, ((static_cast<unsigned>(PropertyAttribute::ReadOnly|PropertyAttribute::Builtin|PropertyAttribute::Accessor|PropertyAttribute::Function)) & ~PropertyAttribute::Function) | PropertyAttribute::Builtin, NoIntrinsic, { HashTableValue::BuiltinGeneratorType, processObjectMainModuleCodeGenerator, 0 } }, { "memoryUsage"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructMemoryUsage } }, { "moduleLoadList"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, Process_stubEmptyArray } }, - { "nextTick"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionNextTick, 1 } }, + { "nextTick"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructProcessNextTickFn } }, { "openStdin"_s, static_cast<unsigned>(PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, Process_functionOpenStdin, 0 } }, { "pid"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructPid } }, { "platform"_s, static_cast<unsigned>(PropertyAttribute::PropertyCallback), NoIntrinsic, { HashTableValue::LazyPropertyType, constructPlatform } }, diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index 7edbd42e6..286084b4d 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -130,6 +130,7 @@ #endif #include "BunObject.h" +#include "JSNextTickQueue.h" using namespace Bun; @@ -294,7 +295,7 @@ extern "C" void JSCInitialize(const char* envp[], size_t envc, void (*onCrash)(c } extern "C" void* Bun__getVM(); -extern "C" JSGlobalObject* Bun__getDefaultGlobal(); +extern "C" Zig::GlobalObject* Bun__getDefaultGlobal(); // Error.captureStackTrace may cause computeErrorInfo to be called twice // Rather than figure out the plumbing in JSC, we just skip the next call @@ -432,6 +433,44 @@ static String computeErrorInfo(JSC::VM& vm, Vector<StackFrame>& stackTrace, unsi return computeErrorInfoWithoutPrepareStackTrace(vm, stackTrace, line, column, sourceURL, errorInstance); } +static void resetOnEachMicrotaskTick(JSC::VM& vm, Zig::GlobalObject* globalObject); + +static void checkIfNextTickWasCalledDuringMicrotask(JSC::VM& vm) +{ + auto* globalObject = Bun__getDefaultGlobal(); + if (auto nextTickQueueValue = globalObject->m_nextTickQueue.get()) { + auto* queue = jsCast<Bun::JSNextTickQueue*>(nextTickQueueValue); + resetOnEachMicrotaskTick(vm, globalObject); + queue->drain(vm, globalObject); + } +} + +static void cleanupAsyncHooksData(JSC::VM& vm) +{ + auto* globalObject = Bun__getDefaultGlobal(); + globalObject->m_asyncContextData.get()->putInternalField(vm, 0, jsUndefined()); + globalObject->asyncHooksNeedsCleanup = false; + if (!globalObject->m_nextTickQueue) { + vm.setOnEachMicrotaskTick(&checkIfNextTickWasCalledDuringMicrotask); + checkIfNextTickWasCalledDuringMicrotask(vm); + } else { + vm.setOnEachMicrotaskTick(nullptr); + } +} + +static void resetOnEachMicrotaskTick(JSC::VM& vm, Zig::GlobalObject* globalObject) +{ + if (globalObject->asyncHooksNeedsCleanup) { + vm.setOnEachMicrotaskTick(&cleanupAsyncHooksData); + } else { + if (globalObject->m_nextTickQueue) { + vm.setOnEachMicrotaskTick(nullptr); + } else { + vm.setOnEachMicrotaskTick(&checkIfNextTickWasCalledDuringMicrotask); + } + } +} + extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, int32_t executionContextId, bool miniMode, void* worker_ptr) { auto heapSize = miniMode ? JSC::HeapType::Small : JSC::HeapType::Large; @@ -479,6 +518,16 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, JSC::gcProtect(globalObject); + vm.setOnEachMicrotaskTick([](JSC::VM& vm) -> void { + auto* globalObject = Bun__getDefaultGlobal(); + if (auto nextTickQueue = globalObject->m_nextTickQueue.get()) { + resetOnEachMicrotaskTick(vm, globalObject); + Bun::JSNextTickQueue* queue = jsCast<Bun::JSNextTickQueue*>(nextTickQueue); + queue->drain(vm, globalObject); + return; + } + }); + vm.ref(); return globalObject; } @@ -1115,6 +1164,17 @@ JSC_DEFINE_HOST_FUNCTION(functionSetTimeout, return JSC::JSValue::encode(JSC::JSValue {}); } +#ifdef BUN_DEBUG + /** View the file name of the JS file that called this function + * from a debugger */ + SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm); + const char* fileName = sourceOrigin.string().utf8().data(); + static const char* lastFileName = nullptr; + if (lastFileName != fileName) { + lastFileName = fileName; + } +#endif + return Bun__Timer__setTimeout(globalObject, JSC::JSValue::encode(job), JSC::JSValue::encode(num), JSValue::encode(arguments)); } @@ -1166,6 +1226,17 @@ JSC_DEFINE_HOST_FUNCTION(functionSetInterval, return JSC::JSValue::encode(JSC::JSValue {}); } +#ifdef BUN_DEBUG + /** View the file name of the JS file that called this function + * from a debugger */ + SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm); + const char* fileName = sourceOrigin.string().utf8().data(); + static const char* lastFileName = nullptr; + if (lastFileName != fileName) { + lastFileName = fileName; + } +#endif + return Bun__Timer__setInterval(globalObject, JSC::JSValue::encode(job), JSC::JSValue::encode(num), JSValue::encode(arguments)); } @@ -1182,6 +1253,17 @@ JSC_DEFINE_HOST_FUNCTION(functionClearInterval, JSC::JSValue num = callFrame->argument(0); +#ifdef BUN_DEBUG + /** View the file name of the JS file that called this function + * from a debugger */ + SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm); + const char* fileName = sourceOrigin.string().utf8().data(); + static const char* lastFileName = nullptr; + if (lastFileName != fileName) { + lastFileName = fileName; + } +#endif + return Bun__Timer__clearInterval(globalObject, JSC::JSValue::encode(num)); } @@ -1198,6 +1280,17 @@ JSC_DEFINE_HOST_FUNCTION(functionClearTimeout, JSC::JSValue num = callFrame->argument(0); +#ifdef BUN_DEBUG + /** View the file name of the JS file that called this function + * from a debugger */ + SourceOrigin sourceOrigin = callFrame->callerSourceOrigin(vm); + const char* fileName = sourceOrigin.string().utf8().data(); + static const char* lastFileName = nullptr; + if (lastFileName != fileName) { + lastFileName = fileName; + } +#endif + return Bun__Timer__clearTimeout(globalObject, JSC::JSValue::encode(num)); } @@ -1393,12 +1486,6 @@ JSC_DEFINE_HOST_FUNCTION(functionCallback, (JSC::JSGlobalObject * globalObject, return JSC::JSValue::encode(JSC::call(globalObject, callback, callData, JSC::jsUndefined(), JSC::MarkedArgumentBuffer())); } -static void cleanupAsyncHooksData(JSC::VM& vm) -{ - vm.setOnEachMicrotaskTick(nullptr); - Bun__getDefaultGlobal()->m_asyncContextData.get()->putInternalField(vm, 0, jsUndefined()); -} - // $lazy("async_hooks").cleanupLater JSC_DEFINE_HOST_FUNCTION(asyncHooksCleanupLater, (JSC::JSGlobalObject * globalObject, JSC::CallFrame* callFrame)) { @@ -1406,7 +1493,9 @@ JSC_DEFINE_HOST_FUNCTION(asyncHooksCleanupLater, (JSC::JSGlobalObject * globalOb // - nobody else uses setOnEachMicrotaskTick // - this is called by js if we set async context in a way we may not clear it // - AsyncLocalStorage.prototype.run cleans up after itself and does not call this cb - globalObject->vm().setOnEachMicrotaskTick(&cleanupAsyncHooksData); + auto* global = jsCast<Zig::GlobalObject*>(globalObject); + global->asyncHooksNeedsCleanup = true; + resetOnEachMicrotaskTick(globalObject->vm(), global); return JSC::JSValue::encode(JSC::jsUndefined()); } @@ -3955,6 +4044,23 @@ extern "C" bool JSC__JSGlobalObject__startRemoteInspector(JSC__JSGlobalObject* g #endif } +void GlobalObject::drainMicrotasks() +{ + auto& vm = this->vm(); + if (auto nextTickQueue = this->m_nextTickQueue.get()) { + Bun::JSNextTickQueue* queue = jsCast<Bun::JSNextTickQueue*>(nextTickQueue); + queue->drain(vm, this); + return; + } + + vm.drainMicrotasks(); +} + +extern "C" void JSC__JSGlobalObject__drainMicrotasks(Zig::GlobalObject* globalObject) +{ + globalObject->drainMicrotasks(); +} + template<typename Visitor> void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) { @@ -4008,6 +4114,8 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor) visitor.append(thisObject->m_JSWebSocketSetterValue); visitor.append(thisObject->m_JSWorkerSetterValue); + visitor.append(thisObject->m_nextTickQueue); + thisObject->m_JSArrayBufferSinkClassStructure.visit(visitor); thisObject->m_JSBufferListClassStructure.visit(visitor); thisObject->m_JSFFIFunctionStructure.visit(visitor); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 029f90132..e622016de 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -296,6 +296,8 @@ public: return m_processEnvObject.getInitializedOnMainThread(this); } + void drainMicrotasks(); + void handleRejectedPromises(); void initGeneratedLazyClasses(); @@ -363,6 +365,8 @@ public: return func; } + bool asyncHooksNeedsCleanup = false; + /** * WARNING: You must update visitChildrenImpl() if you add a new field. * @@ -381,6 +385,7 @@ public: mutable WriteBarrier<JSFunction> m_readableStreamToText; mutable WriteBarrier<JSFunction> m_readableStreamToFormData; + mutable WriteBarrier<Unknown> m_nextTickQueue; mutable WriteBarrier<Unknown> m_BunCommonJSModuleValue; mutable WriteBarrier<Unknown> m_JSBroadcastChannelSetterValue; mutable WriteBarrier<Unknown> m_JSBufferSetterValue; diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index ca072f2b1..6413e0470 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -2527,7 +2527,6 @@ JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* globalObject, const BunString* arg1) { - globalObject->vm().drainMicrotasks(); auto name = Bun::toWTFString(*arg1); name.impl()->ref(); @@ -2546,9 +2545,7 @@ JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* globalObject, JSC::JSInternalPromise::rejectedPromise(globalObject, callFrame->argument(0))); }); - globalObject->vm().drainMicrotasks(); auto result = promise->then(globalObject, resolverFunction, rejecterFunction); - globalObject->vm().drainMicrotasks(); // if (promise->status(globalObject->vm()) == // JSC::JSPromise::Status::Fulfilled) { diff --git a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h index 4c09df6a5..e21a62bf8 100644 --- a/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMClientIsoSubspaces.h @@ -42,6 +42,7 @@ public: std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForProcessObject; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForInternalModuleRegistry; std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForBunInspectorConnection; + std::unique_ptr<GCClient::IsoSubspace> m_clientSubspaceForJSNextTickQueue; #include "ZigGeneratedClasses+DOMClientIsoSubspaces.h" /* --- bun --- */ diff --git a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h index 2b834cf3c..806aa4454 100644 --- a/src/bun.js/bindings/webcore/DOMIsoSubspaces.h +++ b/src/bun.js/bindings/webcore/DOMIsoSubspaces.h @@ -42,7 +42,7 @@ public: std::unique_ptr<IsoSubspace> m_subspaceForProcessObject; std::unique_ptr<IsoSubspace> m_subspaceForInternalModuleRegistry; std::unique_ptr<IsoSubspace> m_subspaceForBunInspectorConnection; - + std::unique_ptr<IsoSubspace> m_subspaceForJSNextTickQueue; #include "ZigGeneratedClasses+DOMIsoSubspaces.h" /*-- BUN --*/ diff --git a/src/bun.js/event_loop.zig b/src/bun.js/event_loop.zig index 640a9276c..f1367c239 100644 --- a/src/bun.js/event_loop.zig +++ b/src/bun.js/event_loop.zig @@ -529,14 +529,14 @@ pub const EventLoop = struct { this.virtual_machine.event_loop_handle.?.tick(); } } - - pub fn drainMicrotasksWithVM(this: *EventLoop, vm: *JSC.VM) void { - vm.drainMicrotasks(); + extern fn JSC__JSGlobalObject__drainMicrotasks(*JSC.JSGlobalObject) void; + fn drainMicrotasksWithGlobal(this: *EventLoop, globalObject: *JSC.JSGlobalObject) void { + JSC__JSGlobalObject__drainMicrotasks(globalObject); this.drainDeferredTasks(); } pub fn drainMicrotasks(this: *EventLoop) void { - this.drainMicrotasksWithVM(this.global.vm()); + this.drainMicrotasksWithGlobal(this.global); } pub fn ensureAliveForOneTick(this: *EventLoop) void { @@ -666,7 +666,7 @@ pub const EventLoop = struct { } global_vm.releaseWeakRefs(); - this.drainMicrotasksWithVM(global_vm); + this.drainMicrotasksWithGlobal(global); } this.tasks.head = if (this.tasks.count == 0) 0 else this.tasks.head; @@ -824,13 +824,14 @@ pub const EventLoop = struct { this.processGCTimer(); - var global_vm = ctx.global.vm(); + var global = ctx.global; + var global_vm = global.vm(); while (true) { while (this.tickWithCount() > 0) : (this.global.handleRejectedPromises()) { this.tickConcurrent(); } else { global_vm.releaseWeakRefs(); - this.drainMicrotasksWithVM(global_vm); + this.drainMicrotasksWithGlobal(global); this.tickConcurrent(); if (this.tasks.count > 0) continue; } |