Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

process signal events #3569

Merged
merged 25 commits into from
Jul 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/bun.js/bindings/JSBundlerPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ class JSBundlerPlugin final : public JSC::JSNonFinalObject {
JSC::LazyProperty<JSBundlerPlugin, JSC::JSFunction> setupFunction;

private:
JSBundlerPlugin(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure, void* config, BunPluginTarget target, JSBundlerPluginAddErrorCallback addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync)
JSBundlerPlugin(JSC::VM& vm, JSC::JSGlobalObject*, JSC::Structure* structure, void* config, BunPluginTarget target,
JSBundlerPluginAddErrorCallback addError, JSBundlerPluginOnLoadAsyncCallback onLoadAsync, JSBundlerPluginOnResolveAsyncCallback onResolveAsync)
: JSC::JSNonFinalObject(vm, structure)
, plugin(BundlerPlugin(config, target, addError, onLoadAsync, onResolveAsync))
{
Expand Down
290 changes: 208 additions & 82 deletions src/bun.js/bindings/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,91 +437,215 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionChdir,
return JSC::JSValue::encode(result);
}

// static const NeverDestroyed<String> signalNames[] = {
// MAKE_STATIC_STRING_IMPL("SIGHUP"),
// MAKE_STATIC_STRING_IMPL("SIGINT"),
// MAKE_STATIC_STRING_IMPL("SIGQUIT"),
// MAKE_STATIC_STRING_IMPL("SIGILL"),
// MAKE_STATIC_STRING_IMPL("SIGTRAP"),
// MAKE_STATIC_STRING_IMPL("SIGABRT"),
// MAKE_STATIC_STRING_IMPL("SIGIOT"),
// MAKE_STATIC_STRING_IMPL("SIGBUS"),
// MAKE_STATIC_STRING_IMPL("SIGFPE"),
// MAKE_STATIC_STRING_IMPL("SIGKILL"),
// MAKE_STATIC_STRING_IMPL("SIGUSR1"),
// MAKE_STATIC_STRING_IMPL("SIGSEGV"),
// MAKE_STATIC_STRING_IMPL("SIGUSR2"),
// MAKE_STATIC_STRING_IMPL("SIGPIPE"),
// MAKE_STATIC_STRING_IMPL("SIGALRM"),
// MAKE_STATIC_STRING_IMPL("SIGTERM"),
// MAKE_STATIC_STRING_IMPL("SIGCHLD"),
// MAKE_STATIC_STRING_IMPL("SIGCONT"),
// MAKE_STATIC_STRING_IMPL("SIGSTOP"),
// MAKE_STATIC_STRING_IMPL("SIGTSTP"),
// MAKE_STATIC_STRING_IMPL("SIGTTIN"),
// MAKE_STATIC_STRING_IMPL("SIGTTOU"),
// MAKE_STATIC_STRING_IMPL("SIGURG"),
// MAKE_STATIC_STRING_IMPL("SIGXCPU"),
// MAKE_STATIC_STRING_IMPL("SIGXFSZ"),
// MAKE_STATIC_STRING_IMPL("SIGVTALRM"),
// MAKE_STATIC_STRING_IMPL("SIGPROF"),
// MAKE_STATIC_STRING_IMPL("SIGWINCH"),
// MAKE_STATIC_STRING_IMPL("SIGIO"),
// MAKE_STATIC_STRING_IMPL("SIGINFO"),
// MAKE_STATIC_STRING_IMPL("SIGSYS"),
// };
// static const int signalNumbers[] = {
// SIGHUP,
// SIGINT,
// SIGQUIT,
// SIGILL,
// SIGTRAP,
// SIGABRT,
// SIGIOT,
// SIGBUS,
// SIGFPE,
// SIGKILL,
// SIGUSR1,
// SIGSEGV,
// SIGUSR2,
// SIGPIPE,
// SIGALRM,
// SIGTERM,
// SIGCHLD,
// SIGCONT,
// SIGSTOP,
// SIGTSTP,
// SIGTTIN,
// SIGTTOU,
// SIGURG,
// SIGXCPU,
// SIGXFSZ,
// SIGVTALRM,
// SIGPROF,
// SIGWINCH,
// SIGIO,
// SIGINFO,
// SIGSYS,
// };

// JSC_DEFINE_HOST_FUNCTION(jsFunctionProcessOn, (JSGlobalObject * globalObject, CallFrame* callFrame))
// {
// VM& vm = globalObject->vm();
// auto scope = DECLARE_THROW_SCOPE(vm);

// if (callFrame->argumentCount() < 2) {
// throwVMError(globalObject, scope, "Not enough arguments"_s);
// return JSValue::encode(jsUndefined());
// }

// String eventName = callFrame->uncheckedArgument(0).toWTFString(globalObject);
// RETURN_IF_EXCEPTION(scope, encodedJSValue());
// }
static HashMap<String, int>* signalNameToNumberMap = nullptr;
static HashMap<int, String>* signalNumberToNameMap = nullptr;

// signal number to array of script execution context ids that care about the signal
static HashMap<int, HashSet<uint32_t>>* signalToContextIdsMap = nullptr;
static Lock signalToContextIdsMapLock;

static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& eventName, bool isAdded)
{

static const NeverDestroyed<String> signalNames[] = {
MAKE_STATIC_STRING_IMPL("SIGHUP"),
MAKE_STATIC_STRING_IMPL("SIGINT"),
MAKE_STATIC_STRING_IMPL("SIGQUIT"),
MAKE_STATIC_STRING_IMPL("SIGILL"),
MAKE_STATIC_STRING_IMPL("SIGTRAP"),
MAKE_STATIC_STRING_IMPL("SIGABRT"),
MAKE_STATIC_STRING_IMPL("SIGIOT"),
MAKE_STATIC_STRING_IMPL("SIGBUS"),
MAKE_STATIC_STRING_IMPL("SIGFPE"),
MAKE_STATIC_STRING_IMPL("SIGKILL"),
MAKE_STATIC_STRING_IMPL("SIGUSR1"),
MAKE_STATIC_STRING_IMPL("SIGSEGV"),
MAKE_STATIC_STRING_IMPL("SIGUSR2"),
MAKE_STATIC_STRING_IMPL("SIGPIPE"),
MAKE_STATIC_STRING_IMPL("SIGALRM"),
MAKE_STATIC_STRING_IMPL("SIGTERM"),
MAKE_STATIC_STRING_IMPL("SIGCHLD"),
MAKE_STATIC_STRING_IMPL("SIGCONT"),
MAKE_STATIC_STRING_IMPL("SIGSTOP"),
MAKE_STATIC_STRING_IMPL("SIGTSTP"),
MAKE_STATIC_STRING_IMPL("SIGTTIN"),
MAKE_STATIC_STRING_IMPL("SIGTTOU"),
MAKE_STATIC_STRING_IMPL("SIGURG"),
MAKE_STATIC_STRING_IMPL("SIGXCPU"),
MAKE_STATIC_STRING_IMPL("SIGXFSZ"),
MAKE_STATIC_STRING_IMPL("SIGVTALRM"),
MAKE_STATIC_STRING_IMPL("SIGPROF"),
MAKE_STATIC_STRING_IMPL("SIGWINCH"),
MAKE_STATIC_STRING_IMPL("SIGIO"),
MAKE_STATIC_STRING_IMPL("SIGINFO"),
MAKE_STATIC_STRING_IMPL("SIGSYS"),
};

static std::once_flag signalNameToNumberMapOnceFlag;
std::call_once(signalNameToNumberMapOnceFlag, [] {
signalNameToNumberMap = new HashMap<String, int>();
signalNameToNumberMap->reserveInitialCapacity(31);
signalNameToNumberMap->add(signalNames[0], SIGHUP);
signalNameToNumberMap->add(signalNames[1], SIGINT);
signalNameToNumberMap->add(signalNames[2], SIGQUIT);
signalNameToNumberMap->add(signalNames[3], SIGILL);
signalNameToNumberMap->add(signalNames[4], SIGTRAP);
signalNameToNumberMap->add(signalNames[5], SIGABRT);
signalNameToNumberMap->add(signalNames[6], SIGIOT);
signalNameToNumberMap->add(signalNames[7], SIGBUS);
signalNameToNumberMap->add(signalNames[8], SIGFPE);
// signalNameToNumberMap->add(signalNames[9], SIGKILL);
signalNameToNumberMap->add(signalNames[10], SIGUSR1);
signalNameToNumberMap->add(signalNames[11], SIGSEGV);
signalNameToNumberMap->add(signalNames[12], SIGUSR2);
signalNameToNumberMap->add(signalNames[13], SIGPIPE);
signalNameToNumberMap->add(signalNames[14], SIGALRM);
signalNameToNumberMap->add(signalNames[15], SIGTERM);
signalNameToNumberMap->add(signalNames[16], SIGCHLD);
signalNameToNumberMap->add(signalNames[17], SIGCONT);
// signalNameToNumberMap->add(signalNames[18], SIGSTOP);
signalNameToNumberMap->add(signalNames[19], SIGTSTP);
signalNameToNumberMap->add(signalNames[20], SIGTTIN);
signalNameToNumberMap->add(signalNames[21], SIGTTOU);
signalNameToNumberMap->add(signalNames[22], SIGURG);
signalNameToNumberMap->add(signalNames[23], SIGXCPU);
signalNameToNumberMap->add(signalNames[24], SIGXFSZ);
signalNameToNumberMap->add(signalNames[25], SIGVTALRM);
signalNameToNumberMap->add(signalNames[26], SIGPROF);
signalNameToNumberMap->add(signalNames[27], SIGWINCH);
signalNameToNumberMap->add(signalNames[28], SIGIO);
#ifdef SIGINFO
signalNameToNumberMap->add(signalNames[29], SIGINFO);
#endif

#ifndef SIGINFO
signalNameToNumberMap->add(signalNames[29], 255);
#endif
signalNameToNumberMap->add(signalNames[30], SIGSYS);
});

static std::once_flag signalNumberToNameMapOnceFlag;
std::call_once(signalNumberToNameMapOnceFlag, [] {
signalNumberToNameMap = new HashMap<int, String>();
signalNumberToNameMap->reserveInitialCapacity(31);
signalNumberToNameMap->add(SIGHUP, signalNames[0]);
signalNumberToNameMap->add(SIGINT, signalNames[1]);
signalNumberToNameMap->add(SIGQUIT, signalNames[2]);
signalNumberToNameMap->add(SIGILL, signalNames[3]);
signalNumberToNameMap->add(SIGTRAP, signalNames[4]);
signalNumberToNameMap->add(SIGABRT, signalNames[5]);
signalNumberToNameMap->add(SIGIOT, signalNames[6]);
signalNumberToNameMap->add(SIGBUS, signalNames[7]);
signalNumberToNameMap->add(SIGFPE, signalNames[8]);
// signalNumberToNameMap->add(SIGKILL, signalNames[9]);
signalNumberToNameMap->add(SIGUSR1, signalNames[10]);
signalNumberToNameMap->add(SIGSEGV, signalNames[11]);
signalNumberToNameMap->add(SIGUSR2, signalNames[12]);
signalNumberToNameMap->add(SIGPIPE, signalNames[13]);
signalNumberToNameMap->add(SIGALRM, signalNames[14]);
signalNumberToNameMap->add(SIGTERM, signalNames[15]);
signalNumberToNameMap->add(SIGCHLD, signalNames[16]);
signalNumberToNameMap->add(SIGCONT, signalNames[17]);
// signalNumberToNameMap->add(SIGSTOP, signalNames[18]);
signalNumberToNameMap->add(SIGTSTP, signalNames[19]);
signalNumberToNameMap->add(SIGTTIN, signalNames[20]);
signalNumberToNameMap->add(SIGTTOU, signalNames[21]);
signalNumberToNameMap->add(SIGURG, signalNames[22]);
signalNumberToNameMap->add(SIGXCPU, signalNames[23]);
signalNumberToNameMap->add(SIGXFSZ, signalNames[24]);
signalNumberToNameMap->add(SIGVTALRM, signalNames[25]);
signalNumberToNameMap->add(SIGPROF, signalNames[26]);
signalNumberToNameMap->add(SIGWINCH, signalNames[27]);
signalNumberToNameMap->add(SIGIO, signalNames[28]);
#ifdef SIGINFO
signalNameToNumberMap->add(signalNames[29], SIGINFO);
#endif
signalNumberToNameMap->add(SIGSYS, signalNames[30]);
});

if (!signalToContextIdsMap) {
signalToContextIdsMap = new HashMap<int, HashSet<uint32_t>>();
}

if (isAdded) {
if (auto signalNumber = signalNameToNumberMap->get(eventName.string())) {
uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier();
Locker lock { signalToContextIdsMapLock };
if (!signalToContextIdsMap->contains(signalNumber)) {
HashSet<uint32_t> contextIds;
contextIds.add(contextId);
signalToContextIdsMap->set(signalNumber, contextIds);

lock.unlockEarly();

struct sigaction action;
memset(&action, 0, sizeof(struct sigaction));

// Set the handler in the action struct
action.sa_handler = [](int signalNumber) {
if (UNLIKELY(signalNumberToNameMap->find(signalNumber) == signalNumberToNameMap->end()))
return;

Locker lock { signalToContextIdsMapLock };
if (UNLIKELY(signalToContextIdsMap->find(signalNumber) == signalToContextIdsMap->end()))
return;
auto contextIds = signalToContextIdsMap->get(signalNumber);

for (int contextId : contextIds) {
auto* context = ScriptExecutionContext::getScriptExecutionContext(contextId);
if (UNLIKELY(!context))
continue;

JSGlobalObject* lexicalGlobalObject = context->jsGlobalObject();
Zig::GlobalObject* globalObject = static_cast<Zig::GlobalObject*>(lexicalGlobalObject);

Process* process = jsCast<Process*>(globalObject->processObject());

context->postCrossThreadTask(*process, &Process::emitSignalEvent, signalNumber);
}
};

// Clear the sa_mask
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask, signalNumber);
action.sa_flags = SA_RESTART;

sigaction(signalNumber, &action, nullptr);
} else {
auto contextIds = signalToContextIdsMap->get(signalNumber);
contextIds.add(contextId);
signalToContextIdsMap->set(signalNumber, contextIds);
}
}
} else {
if (auto signalNumber = signalNameToNumberMap->get(eventName.string())) {
uint32_t contextId = eventEmitter.scriptExecutionContext()->identifier();
Locker lock { signalToContextIdsMapLock };
if (signalToContextIdsMap->find(signalNumber) != signalToContextIdsMap->end()) {
HashSet<uint32_t> contextIds = signalToContextIdsMap->get(signalNumber);
contextIds.remove(contextId);
if (contextIds.isEmpty()) {
signal(signalNumber, SIG_DFL);
signalToContextIdsMap->remove(signalNumber);
} else {
signalToContextIdsMap->set(signalNumber, contextIds);
}
}
}
}
}

void Process::emitSignalEvent(int signalNumber)
{
String signalName = signalNumberToNameMap->get(signalNumber);
Identifier signalNameIdentifier = Identifier::fromString(vm(), signalName);
MarkedArgumentBuffer args;
args.append(jsNumber(signalNumber));
wrapped().emitForBindings(signalNameIdentifier, args);
}

Process::~Process()
{
for (auto& listener : this->wrapped().eventListenerMap().entries()) {
}
}

JSC_DEFINE_HOST_FUNCTION(Process_functionAbort, (JSGlobalObject * globalObject, CallFrame*))
Expand Down Expand Up @@ -1550,6 +1674,8 @@ void Process::finishCreation(JSC::VM& vm)
{
Base::finishCreation(vm);

this->wrapped().onDidChangeListener = &onDidChangeListeners;

this->cpuUsageStructure.initLater([](const JSC::LazyProperty<JSC::JSObject, JSC::Structure>::Initializer& init) {
init.set(constructCPUUsageStructure(init.vm, init.owner->globalObject()));
});
Expand Down
2 changes: 2 additions & 0 deletions src/bun.js/bindings/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class Process : public WebCore::JSEventEmitter {
{
}

void emitSignalEvent(int signalNumber);

DECLARE_EXPORT_INFO;

static void destroy(JSC::JSCell* cell)
Expand Down
6 changes: 6 additions & 0 deletions src/bun.js/bindings/ScriptExecutionContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ static HashMap<ScriptExecutionContextIdentifier, ScriptExecutionContext*>& allSc
return contexts;
}

ScriptExecutionContext* ScriptExecutionContext::getScriptExecutionContext(ScriptExecutionContextIdentifier identifier)
{
Locker locker { allScriptExecutionContextsMapLock };
return allScriptExecutionContextsMap().get(identifier);
}

template<bool SSL, bool isServer>
static void registerHTTPContextForWebSocket(ScriptExecutionContext* script, us_socket_context_t* ctx, us_loop_t* loop)
{
Expand Down
8 changes: 7 additions & 1 deletion src/bun.js/bindings/ScriptExecutionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,12 @@ class ScriptExecutionContext : public CanMakeWeakPtr<ScriptExecutionContext> {
}
}

const WTF::URL& url() const { return m_url; }
static ScriptExecutionContext* getScriptExecutionContext(ScriptExecutionContextIdentifier identifier);

const WTF::URL& url() const
{
return m_url;
}
bool activeDOMObjectsAreSuspended() { return false; }
bool activeDOMObjectsAreStopped() { return false; }
bool isContextThread() { return true; }
Expand Down Expand Up @@ -141,6 +146,7 @@ class ScriptExecutionContext : public CanMakeWeakPtr<ScriptExecutionContext> {
auto* task = new EventLoopTask(WTFMove(lambda));
postTaskOnTimeout(task, timeout);
}

template<typename... Arguments>
void postCrossThreadTask(Arguments&&... arguments)
{
Expand Down
7 changes: 7 additions & 0 deletions src/bun.js/bindings/webcore/EventEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ bool EventEmitter::addListener(const Identifier& eventType, Ref<EventListener>&&
}

eventListenersDidChange();
if (this->onDidChangeListener)
this->onDidChangeListener(*this, eventType, true);
return true;
}

Expand Down Expand Up @@ -62,6 +64,9 @@ bool EventEmitter::removeListener(const Identifier& eventType, EventListener& li

if (data->eventListenerMap.remove(eventType, listener)) {
eventListenersDidChange();

if (this->onDidChangeListener)
this->onDidChangeListener(*this, eventType, false);
return true;
}
return false;
Expand Down Expand Up @@ -93,6 +98,8 @@ bool EventEmitter::removeAllListeners(const Identifier& eventType)

if (data->eventListenerMap.removeAll(eventType)) {
eventListenersDidChange();
if (this->onDidChangeListener)
this->onDidChangeListener(*this, eventType, false);
return true;
}
return false;
Expand Down
Loading
Loading