Skip to content

Commit c664214

Browse files
committed
src: make Environment::interrupt_data_ atomic
Otherwise this potentially leads to race conditions when used in a multi-threaded environment (i.e. when used for its very purpose). PR-URL: #32523 Reviewed-By: Matheus Marchini <mat@mmarchini.me> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent d0a3bf1 commit c664214

File tree

2 files changed

+19
-7
lines changed

2 files changed

+19
-7
lines changed

src/env.cc

+18-6
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,8 @@ Environment::Environment(IsolateData* isolate_data,
435435
}
436436

437437
Environment::~Environment() {
438-
if (interrupt_data_ != nullptr) *interrupt_data_ = nullptr;
438+
if (Environment** interrupt_data = interrupt_data_.load())
439+
*interrupt_data = nullptr;
439440

440441
// FreeEnvironment() should have set this.
441442
CHECK(is_stopping());
@@ -768,12 +769,23 @@ void Environment::RunAndClearNativeImmediates(bool only_refed) {
768769
}
769770

770771
void Environment::RequestInterruptFromV8() {
771-
if (interrupt_data_ != nullptr) return; // Already scheduled.
772-
773772
// The Isolate may outlive the Environment, so some logic to handle the
774773
// situation in which the Environment is destroyed before the handler runs
775774
// is required.
776-
interrupt_data_ = new Environment*(this);
775+
776+
// We allocate a new pointer to a pointer to this Environment instance, and
777+
// try to set it as interrupt_data_. If interrupt_data_ was already set, then
778+
// callbacks are already scheduled to run and we can delete our own pointer
779+
// and just return. If it was nullptr previously, the Environment** is stored;
780+
// ~Environment sets the Environment* contained in it to nullptr, so that
781+
// the callback can check whether ~Environment has already run and it is thus
782+
// not safe to access the Environment instance itself.
783+
Environment** interrupt_data = new Environment*(this);
784+
Environment** dummy = nullptr;
785+
if (!interrupt_data_.compare_exchange_strong(dummy, interrupt_data)) {
786+
delete interrupt_data;
787+
return; // Already scheduled.
788+
}
777789

778790
isolate()->RequestInterrupt([](Isolate* isolate, void* data) {
779791
std::unique_ptr<Environment*> env_ptr { static_cast<Environment**>(data) };
@@ -784,9 +796,9 @@ void Environment::RequestInterruptFromV8() {
784796
// handled during cleanup.
785797
return;
786798
}
787-
env->interrupt_data_ = nullptr;
799+
env->interrupt_data_.store(nullptr);
788800
env->RunAndClearInterrupts();
789-
}, interrupt_data_);
801+
}, interrupt_data);
790802
}
791803

792804
void Environment::ScheduleTimer(int64_t duration_ms) {

src/env.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -1423,7 +1423,7 @@ class Environment : public MemoryRetainer {
14231423

14241424
void RunAndClearNativeImmediates(bool only_refed = false);
14251425
void RunAndClearInterrupts();
1426-
Environment** interrupt_data_ = nullptr;
1426+
std::atomic<Environment**> interrupt_data_ {nullptr};
14271427
void RequestInterruptFromV8();
14281428
static void CheckImmediate(uv_check_t* handle);
14291429

0 commit comments

Comments
 (0)