Skip to content

Commit b0ce668

Browse files
addaleaxMylesBorins
authored andcommitted
src: add interrupts to Environments/Workers
Allow doing what V8’s `v8::Isolate::RequestInterrupt()` does for V8. This also works when there is no JS code currently executing. PR-URL: nodejs#31386 Refs: openjs-foundation/summit#240 Reviewed-By: Gireesh Punathil <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 3f9d1dd commit b0ce668

File tree

4 files changed

+74
-0
lines changed

4 files changed

+74
-0
lines changed

src/env-inl.h

+12
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,18 @@ void Environment::SetImmediateThreadsafe(Fn&& cb) {
796796
uv_async_send(&task_queues_async_);
797797
}
798798

799+
template <typename Fn>
800+
void Environment::RequestInterrupt(Fn&& cb) {
801+
auto callback = std::make_unique<NativeImmediateCallbackImpl<Fn>>(
802+
std::move(cb), false);
803+
{
804+
Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
805+
native_immediates_interrupts_.Push(std::move(callback));
806+
}
807+
uv_async_send(&task_queues_async_);
808+
RequestInterruptFromV8();
809+
}
810+
799811
Environment::NativeImmediateCallback::NativeImmediateCallback(bool refed)
800812
: refed_(refed) {}
801813

src/env.cc

+41
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,8 @@ Environment::Environment(IsolateData* isolate_data,
387387
}
388388

389389
Environment::~Environment() {
390+
if (interrupt_data_ != nullptr) *interrupt_data_ = nullptr;
391+
390392
isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback(
391393
BuildEmbedderGraph, this);
392394

@@ -653,11 +655,29 @@ void Environment::AtExit(void (*cb)(void* arg), void* arg) {
653655
at_exit_functions_.push_front(ExitCallback{cb, arg});
654656
}
655657

658+
void Environment::RunAndClearInterrupts() {
659+
while (native_immediates_interrupts_.size() > 0) {
660+
NativeImmediateQueue queue;
661+
{
662+
Mutex::ScopedLock lock(native_immediates_threadsafe_mutex_);
663+
queue.ConcatMove(std::move(native_immediates_interrupts_));
664+
}
665+
DebugSealHandleScope seal_handle_scope(isolate());
666+
667+
while (std::unique_ptr<NativeImmediateCallback> head = queue.Shift())
668+
head->Call(this);
669+
}
670+
}
671+
656672
void Environment::RunAndClearNativeImmediates(bool only_refed) {
657673
TraceEventScope trace_scope(TRACING_CATEGORY_NODE1(environment),
658674
"RunAndClearNativeImmediates", this);
659675
size_t ref_count = 0;
660676

677+
// Handle interrupts first. These functions are not allowed to throw
678+
// exceptions, so we do not need to handle that.
679+
RunAndClearInterrupts();
680+
661681
// It is safe to check .size() first, because there is a causal relationship
662682
// between pushes to the threadsafe and this function being called.
663683
// For the common case, it's worth checking the size first before establishing
@@ -697,6 +717,27 @@ void Environment::RunAndClearNativeImmediates(bool only_refed) {
697717
ToggleImmediateRef(false);
698718
}
699719

720+
void Environment::RequestInterruptFromV8() {
721+
if (interrupt_data_ != nullptr) return; // Already scheduled.
722+
723+
// The Isolate may outlive the Environment, so some logic to handle the
724+
// situation in which the Environment is destroyed before the handler runs
725+
// is required.
726+
interrupt_data_ = new Environment*(this);
727+
728+
isolate()->RequestInterrupt([](Isolate* isolate, void* data) {
729+
std::unique_ptr<Environment*> env_ptr { static_cast<Environment**>(data) };
730+
Environment* env = *env_ptr;
731+
if (env == nullptr) {
732+
// The Environment has already been destroyed. That should be okay; any
733+
// callback added before the Environment shuts down would have been
734+
// handled during cleanup.
735+
return;
736+
}
737+
env->interrupt_data_ = nullptr;
738+
env->RunAndClearInterrupts();
739+
}, interrupt_data_);
740+
}
700741

701742
void Environment::ScheduleTimer(int64_t duration_ms) {
702743
if (started_cleanup_) return;

src/env.h

+10
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,12 @@ class Environment : public MemoryRetainer {
11761176
template <typename Fn>
11771177
// This behaves like SetImmediate() but can be called from any thread.
11781178
inline void SetImmediateThreadsafe(Fn&& cb);
1179+
// This behaves like V8's Isolate::RequestInterrupt(), but also accounts for
1180+
// the event loop (i.e. combines the V8 function with SetImmediate()).
1181+
// The passed callback may not throw exceptions.
1182+
// This function can be called from any thread.
1183+
template <typename Fn>
1184+
inline void RequestInterrupt(Fn&& cb);
11791185
// This needs to be available for the JS-land setImmediate().
11801186
void ToggleImmediateRef(bool ref);
11811187

@@ -1426,8 +1432,12 @@ class Environment : public MemoryRetainer {
14261432
NativeImmediateQueue native_immediates_;
14271433
Mutex native_immediates_threadsafe_mutex_;
14281434
NativeImmediateQueue native_immediates_threadsafe_;
1435+
NativeImmediateQueue native_immediates_interrupts_;
14291436

14301437
void RunAndClearNativeImmediates(bool only_refed = false);
1438+
void RunAndClearInterrupts();
1439+
Environment** interrupt_data_ = nullptr;
1440+
void RequestInterruptFromV8();
14311441
static void CheckImmediate(uv_check_t* handle);
14321442

14331443
// Use an unordered_set, so that we have efficient insertion and removal.

src/node_worker.h

+11
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ class Worker : public AsyncWrap {
4444
tracker->TrackField("parent_port", parent_port_);
4545
}
4646

47+
template <typename Fn>
48+
inline bool RequestInterrupt(Fn&& cb);
49+
4750
SET_MEMORY_INFO_NAME(Worker)
4851
SET_SELF_SIZE(Worker)
4952

@@ -125,6 +128,14 @@ class Worker : public AsyncWrap {
125128
friend class WorkerThreadData;
126129
};
127130

131+
template <typename Fn>
132+
bool Worker::RequestInterrupt(Fn&& cb) {
133+
Mutex::ScopedLock lock(mutex_);
134+
if (env_ == nullptr) return false;
135+
env_->RequestInterrupt(std::move(cb));
136+
return true;
137+
}
138+
128139
} // namespace worker
129140
} // namespace node
130141

0 commit comments

Comments
 (0)