Skip to content

Commit ff0a036

Browse files
committed
embedding: provide hook for custom process.exit() behaviour
Embedders may not want to terminate the process when `process.exit()` is called. This provides a hook for more flexible handling of that situation. Refs: #30467 (comment) Backport-PR-URL: #35241 PR-URL: #32531 Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 61eec0c commit ff0a036

File tree

7 files changed

+60
-11
lines changed

7 files changed

+60
-11
lines changed

src/api/environment.cc

+13
Original file line numberDiff line numberDiff line change
@@ -713,4 +713,17 @@ ThreadId AllocateEnvironmentThreadId() {
713713
return ret;
714714
}
715715

716+
void DefaultProcessExitHandler(Environment* env, int exit_code) {
717+
env->set_can_call_into_js(false);
718+
env->stop_sub_worker_contexts();
719+
DisposePlatform();
720+
exit(exit_code);
721+
}
722+
723+
724+
void SetProcessExitHandler(Environment* env,
725+
std::function<void(Environment*, int)>&& handler) {
726+
env->set_process_exit_handler(std::move(handler));
727+
}
728+
716729
} // namespace node

src/env-inl.h

+5
Original file line numberDiff line numberDiff line change
@@ -1221,6 +1221,11 @@ void Environment::set_main_utf16(std::unique_ptr<v8::String::Value> str) {
12211221
main_utf16_ = std::move(str);
12221222
}
12231223

1224+
void Environment::set_process_exit_handler(
1225+
std::function<void(Environment*, int)>&& handler) {
1226+
process_exit_handler_ = std::move(handler);
1227+
}
1228+
12241229
#define VP(PropertyName, StringValue) V(v8::Private, PropertyName)
12251230
#define VY(PropertyName, StringValue) V(v8::Symbol, PropertyName)
12261231
#define VS(PropertyName, StringValue) V(v8::String, PropertyName)

src/env.cc

+1-8
Original file line numberDiff line numberDiff line change
@@ -987,14 +987,7 @@ void Environment::Exit(int exit_code) {
987987
StackTrace::CurrentStackTrace(
988988
isolate(), stack_trace_limit(), StackTrace::kDetailed));
989989
}
990-
if (is_main_thread()) {
991-
set_can_call_into_js(false);
992-
stop_sub_worker_contexts();
993-
DisposePlatform();
994-
exit(exit_code);
995-
} else {
996-
worker_context()->Exit(exit_code);
997-
}
990+
process_exit_handler_(this, exit_code);
998991
}
999992

1000993
void Environment::stop_sub_worker_contexts() {

src/env.h

+5
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,8 @@ class Environment : public MemoryRetainer {
12701270
std::shared_ptr<v8::ArrayBuffer::Allocator>);
12711271

12721272
inline void set_main_utf16(std::unique_ptr<v8::String::Value>);
1273+
inline void set_process_exit_handler(
1274+
std::function<void(Environment*, int)>&& handler);
12731275

12741276
private:
12751277
inline void ThrowError(v8::Local<v8::Value> (*fun)(v8::Local<v8::String>),
@@ -1428,6 +1430,9 @@ class Environment : public MemoryRetainer {
14281430
ArrayBufferAllocatorList;
14291431
ArrayBufferAllocatorList* keep_alive_allocators_ = nullptr;
14301432

1433+
std::function<void(Environment*, int)> process_exit_handler_ {
1434+
DefaultProcessExitHandler };
1435+
14311436
template <typename T>
14321437
void ForEachBaseObject(T&& iterator);
14331438

src/node.h

+12
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,18 @@ NODE_EXTERN v8::MaybeLocal<v8::Value> LoadEnvironment(
452452
std::unique_ptr<InspectorParentHandle> inspector_parent_handle = {});
453453
NODE_EXTERN void FreeEnvironment(Environment* env);
454454

455+
// Set a callback that is called when process.exit() is called from JS,
456+
// overriding the default handler.
457+
// It receives the Environment* instance and the exit code as arguments.
458+
// This could e.g. call Stop(env); in order to terminate execution and stop
459+
// the event loop.
460+
// The default handler disposes of the global V8 platform instance, if one is
461+
// being used, and calls exit().
462+
NODE_EXTERN void SetProcessExitHandler(
463+
Environment* env,
464+
std::function<void(Environment*, int)>&& handler);
465+
NODE_EXTERN void DefaultProcessExitHandler(Environment* env, int exit_code);
466+
455467
// This may return nullptr if context is not associated with a Node instance.
456468
NODE_EXTERN Environment* GetCurrentEnvironment(v8::Local<v8::Context> context);
457469

src/node_worker.cc

+7-3
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ void Worker::Run() {
321321
if (is_stopped()) return;
322322
CHECK_NOT_NULL(env_);
323323
env_->set_env_vars(std::move(env_vars_));
324+
SetProcessExitHandler(env_.get(), [this](Environment*, int exit_code) {
325+
Exit(exit_code);
326+
});
324327
}
325328
{
326329
Mutex::ScopedLock lock(mutex_);
@@ -430,9 +433,10 @@ void Worker::JoinThread() {
430433
MakeCallback(env()->onexit_string(), arraysize(args), args);
431434
}
432435

433-
// We cleared all libuv handles bound to this Worker above,
434-
// the C++ object is no longer needed for anything now.
435-
MakeWeak();
436+
// If we get here, the !thread_joined_ condition at the top of the function
437+
// implies that the thread was running. In that case, its final action will
438+
// be to schedule a callback on the parent thread which will delete this
439+
// object, so there's nothing more to do here.
436440
}
437441

438442
Worker::~Worker() {

test/cctest/test_environment.cc

+17
Original file line numberDiff line numberDiff line change
@@ -448,3 +448,20 @@ TEST_F(EnvironmentTest, InspectorMultipleEmbeddedEnvironments) {
448448
CHECK_EQ(from_inspector->IntegerValue(context).FromJust(), 42);
449449
}
450450
#endif // HAVE_INSPECTOR
451+
452+
TEST_F(EnvironmentTest, ExitHandlerTest) {
453+
const v8::HandleScope handle_scope(isolate_);
454+
const Argv argv;
455+
456+
int callback_calls = 0;
457+
458+
Env env {handle_scope, argv};
459+
SetProcessExitHandler(*env, [&](node::Environment* env_, int exit_code) {
460+
EXPECT_EQ(*env, env_);
461+
EXPECT_EQ(exit_code, 42);
462+
callback_calls++;
463+
node::Stop(*env);
464+
});
465+
node::LoadEnvironment(*env, "process.exit(42)").ToLocalChecked();
466+
EXPECT_EQ(callback_calls, 1);
467+
}

0 commit comments

Comments
 (0)