diff --git a/src/bun.js/bindings/NodeVMModule.cpp b/src/bun.js/bindings/NodeVMModule.cpp index 6c016c1492..fac2765e7f 100644 --- a/src/bun.js/bindings/NodeVMModule.cpp +++ b/src/bun.js/bindings/NodeVMModule.cpp @@ -211,7 +211,6 @@ JSC_DEFINE_HOST_FUNCTION(jsNodeVmModuleEvaluate, (JSC::JSGlobalObject * globalOb if (auto* thisObject = jsDynamicCast(callFrame->thisValue())) { return JSValue::encode(thisObject->evaluate(globalObject, timeout, breakOnSigint)); - // return thisObject->link(globalObject, linker); // } else if (auto* thisObject = jsDynamicCast(callFrame->thisValue())) { // return thisObject->link(globalObject, specifiers, moduleNatives); } else { diff --git a/src/bun.js/bindings/NodeVMScript.cpp b/src/bun.js/bindings/NodeVMScript.cpp index a866ed0b55..21d10ebd39 100644 --- a/src/bun.js/bindings/NodeVMScript.cpp +++ b/src/bun.js/bindings/NodeVMScript.cpp @@ -260,7 +260,7 @@ static JSC::EncodedJSValue runInContext(NodeVMGlobalObject* globalObject, NodeVM globalObject->setContextifiedObject(contextifiedObject); if (options.breakOnSigint) { - SigintWatcher::ensureSigintHandler(); + // TODO(@heimskr): set up a GlobalObjectHolder SigintWatcher::get().registerGlobalObject(globalObject); } @@ -298,6 +298,8 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInThisContext, (JSGlobalObject * globalObject, return ERR::INVALID_ARG_TYPE(throwScope, globalObject, "context"_s, "object"_s, contextArg); } + // TODO(@heimskr): try to retrieve the NodeVMGlobalObject from the context + RunningScriptOptions options; if (!options.fromJS(globalObject, vm, throwScope, contextArg)) { RETURN_IF_EXCEPTION(throwScope, {}); @@ -306,7 +308,6 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInThisContext, (JSGlobalObject * globalObject, if (options.breakOnSigint) { // TODO(@heimskr): register global object as appropriate - SigintWatcher::ensureSigintHandler(); } NakedPtr exception; diff --git a/src/bun.js/bindings/NodeVMSourceTextModule.cpp b/src/bun.js/bindings/NodeVMSourceTextModule.cpp index 2c1d29f8fa..585a121669 100644 --- a/src/bun.js/bindings/NodeVMSourceTextModule.cpp +++ b/src/bun.js/bindings/NodeVMSourceTextModule.cpp @@ -264,6 +264,14 @@ JSValue NodeVMSourceTextModule::evaluate(JSGlobalObject* globalObject, uint32_t run(); } + if (vm.hasPendingTerminationException()) { + scope.clearException(); + vm.clearHasTerminationRequest(); + status(Status::Errored); + throwError(globalObject, scope, ErrorCode::ERR_SCRIPT_EXECUTION_INTERRUPTED, "Script execution was interrupted by `SIGINT`"_s); + return {}; + } + RETURN_IF_EXCEPTION(scope, (status(Status::Errored), JSValue {})); status(Status::Evaluated); return result; diff --git a/src/vm/SigintWatcher.cpp b/src/vm/SigintWatcher.cpp index 910bebf9bb..0e7adf48d0 100644 --- a/src/vm/SigintWatcher.cpp +++ b/src/vm/SigintWatcher.cpp @@ -2,6 +2,7 @@ #include "SigintWatcher.h" extern "C" void Bun__onPosixSignal(int signalNumber); +extern "C" void Bun__ensureSignalHandler(); namespace Bun { @@ -10,7 +11,7 @@ SigintWatcher SigintWatcher::s_instance; SigintWatcher::SigintWatcher() : m_semaphore(1) { - m_globalObjects.reserve(16); + m_globalObjects.reserveInitialCapacity(16); } SigintWatcher::~SigintWatcher() @@ -20,6 +21,25 @@ SigintWatcher::~SigintWatcher() void SigintWatcher::install() { +#if !OS(WINDOWS) + Bun__ensureSignalHandler(); + + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + + action.sa_handler = [](int signalNumber) { + get().signalReceived(); + }; + + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGINT); + action.sa_flags = 0; + + sigaction(SIGINT, &action, nullptr); +#else + static_assert(false, "TODO(@heimskr): implement sigint handler on Windows"); +#endif + if (m_installed.exchange(true)) { return; } @@ -27,6 +47,9 @@ void SigintWatcher::install() m_thread = std::thread([this] { while (m_installed.load()) { bool success = m_semaphore.wait(); + if (!m_installed) { + return; + } ASSERT(success); if (m_waiting.test_and_set()) { m_waiting.clear(); @@ -43,6 +66,17 @@ void SigintWatcher::install() void SigintWatcher::uninstall() { if (m_installed.exchange(false)) { + ASSERT(m_thread.get_id() != std::this_thread::get_id()); + + struct sigaction action; + memset(&action, 0, sizeof(struct sigaction)); + action.sa_handler = SIG_DFL; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGINT); + action.sa_flags = SA_RESTART; + sigaction(SIGINT, &action, nullptr); + + m_semaphore.signal(); m_thread.join(); } } @@ -63,7 +97,7 @@ void SigintWatcher::registerGlobalObject(NodeVMGlobalObject* globalObject) std::unique_lock lock(m_globalObjectsMutex); - m_globalObjects.push_back(globalObject); + m_globalObjects.append(globalObject); } void SigintWatcher::unregisterGlobalObject(NodeVMGlobalObject* globalObject) @@ -80,8 +114,23 @@ void SigintWatcher::unregisterGlobalObject(NodeVMGlobalObject* globalObject) return; } - std::swap(*iter, m_globalObjects.back()); - m_globalObjects.pop_back(); + std::swap(*iter, m_globalObjects.last()); + m_globalObjects.removeLast(); +} + +void SigintWatcher::ref() +{ + if (m_refCount++ == 0) { + install(); + } +} + +void SigintWatcher::deref() +{ + ASSERT(m_refCount > 0); + if (--m_refCount == 0) { + uninstall(); + } } SigintWatcher& SigintWatcher::get() @@ -93,7 +142,7 @@ bool SigintWatcher::signalAll() { std::unique_lock lock(m_globalObjectsMutex); - if (m_globalObjects.empty()) { + if (m_globalObjects.isEmpty()) { return false; } @@ -104,32 +153,4 @@ bool SigintWatcher::signalAll() return true; } -extern "C" void Bun__ensureSignalHandler(); - -void SigintWatcher::ensureSigintHandler() -{ -#if !OS(WINDOWS) - Bun__ensureSignalHandler(); - - struct sigaction action; - memset(&action, 0, sizeof(struct sigaction)); - - // Set the handler in the action struct - action.sa_handler = [](int signalNumber) { - get().signalReceived(); - }; - - // Clear the sa_mask - sigemptyset(&action.sa_mask); - sigaddset(&action.sa_mask, SIGINT); - action.sa_flags = SA_RESTART; - - sigaction(SIGINT, &action, nullptr); - - get().install(); -#else - static_assert(false, "TODO(@heimskr): implement sigint handler on Windows"); -#endif -} - } // namespace Bun diff --git a/src/vm/SigintWatcher.h b/src/vm/SigintWatcher.h index ebd15f93a2..2444859fc5 100644 --- a/src/vm/SigintWatcher.h +++ b/src/vm/SigintWatcher.h @@ -22,6 +22,10 @@ public: void signalReceived(); void registerGlobalObject(NodeVMGlobalObject* globalObject); void unregisterGlobalObject(NodeVMGlobalObject* globalObject); + /** Installs the signal handler if it's not already installed and increments the ref count. */ + void ref(); + /** Decrements the ref count and uninstalls the signal handler if the ref count reaches 0. */ + void deref(); static SigintWatcher& get(); @@ -30,13 +34,18 @@ public: GlobalObjectHolder(NodeVMGlobalObject* globalObject) : m_globalObject(globalObject) { - ensureSigintHandler(); - get().registerGlobalObject(globalObject); + if (m_globalObject) { + get().ref(); + get().registerGlobalObject(globalObject); + } } ~GlobalObjectHolder() { - get().unregisterGlobalObject(m_globalObject); + if (m_globalObject) { + get().unregisterGlobalObject(m_globalObject); + get().deref(); + } } GlobalObjectHolder(const GlobalObjectHolder&) = delete; @@ -53,7 +62,7 @@ public: } private: - NodeVMGlobalObject* m_globalObject; + NodeVMGlobalObject* m_globalObject = nullptr; }; static GlobalObjectHolder hold(NodeVMGlobalObject* globalObject) @@ -61,15 +70,14 @@ public: return { globalObject }; } - static void ensureSigintHandler(); - private: std::thread m_thread; std::atomic_bool m_installed = false; std::atomic_flag m_waiting = false; Semaphore m_semaphore; std::mutex m_globalObjectsMutex; - std::vector m_globalObjects; + WTF::Vector m_globalObjects; + uint32_t m_refCount = 0; bool signalAll();