Skip to content

Commit 6d9aa73

Browse files
committed
src: clean up MultiIsolatePlatform interface
- Since this was introduced, V8 has effectively started requiring that the platform knows of the `Isolate*` before we (or an embedder) create our `IsolateData` structure; therefore, (un)registering it from the `IsolateData` constructor/destructor doesn’t make much sense anymore. - Instead, we can require that the register/unregister functions are only called once, simplifying the implementation a bit. - Add a callback that we can use to know when the platform has cleaned up its resources associated with a given `Isolate`. In particular, this means that in the Worker code, we don’t need to rely on what are essentially guesses about the number of event loop turns that we need in order to have everything cleaned up. PR-URL: #26384 Reviewed-By: Daniel Bevenius <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Joyee Cheung <[email protected]>
1 parent 377c583 commit 6d9aa73

File tree

6 files changed

+59
-37
lines changed

6 files changed

+59
-37
lines changed

src/env.cc

-8
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,6 @@ IsolateData::IsolateData(Isolate* isolate,
8484
uses_node_allocator_(allocator_ == node_allocator_),
8585
platform_(platform) {
8686
CHECK_NOT_NULL(allocator_);
87-
if (platform_ != nullptr)
88-
platform_->RegisterIsolate(isolate_, event_loop);
8987

9088
options_.reset(
9189
new PerIsolateOptions(*(per_process::cli_options->per_isolate)));
@@ -137,12 +135,6 @@ IsolateData::IsolateData(Isolate* isolate,
137135
#undef V
138136
}
139137

140-
IsolateData::~IsolateData() {
141-
if (platform_ != nullptr)
142-
platform_->UnregisterIsolate(isolate_);
143-
}
144-
145-
146138
void InitThreadLocalOnce() {
147139
CHECK_EQ(0, uv_key_create(&Environment::thread_local_env));
148140
}

src/env.h

-1
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,6 @@ class IsolateData {
401401
uv_loop_t* event_loop,
402402
MultiIsolatePlatform* platform = nullptr,
403403
ArrayBufferAllocator* node_allocator = nullptr);
404-
~IsolateData();
405404
inline uv_loop_t* event_loop() const;
406405
inline MultiIsolatePlatform* platform() const;
407406
inline std::shared_ptr<PerIsolateOptions> options();

src/node.h

+13-1
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,22 @@ class NODE_EXTERN MultiIsolatePlatform : public v8::Platform {
228228
virtual void DrainTasks(v8::Isolate* isolate) = 0;
229229
virtual void CancelPendingDelayedTasks(v8::Isolate* isolate) = 0;
230230

231-
// These will be called by the `IsolateData` creation/destruction functions.
231+
// This needs to be called between the calls to `Isolate::Allocate()` and
232+
// `Isolate::Initialize()`, so that initialization can already start
233+
// using the platform.
234+
// When using `NewIsolate()`, this is taken care of by that function.
235+
// This function may only be called once per `Isolate`.
232236
virtual void RegisterIsolate(v8::Isolate* isolate,
233237
struct uv_loop_s* loop) = 0;
238+
// This needs to be called right before calling `Isolate::Dispose()`.
239+
// This function may only be called once per `Isolate`.
234240
virtual void UnregisterIsolate(v8::Isolate* isolate) = 0;
241+
// The platform should call the passed function once all state associated
242+
// with the given isolate has been cleaned up. This can, but does not have to,
243+
// happen asynchronously.
244+
virtual void AddIsolateFinishedCallback(v8::Isolate* isolate,
245+
void (*callback)(void*),
246+
void* data) = 0;
235247
};
236248

237249
// Creates a new isolate with Node.js-specific settings.

src/node_platform.cc

+27-18
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ PerIsolatePlatformData::~PerIsolatePlatformData() {
261261
Shutdown();
262262
}
263263

264+
void PerIsolatePlatformData::AddShutdownCallback(void (*callback)(void*),
265+
void* data) {
266+
shutdown_callbacks_.emplace_back(ShutdownCallback { callback, data });
267+
}
268+
264269
void PerIsolatePlatformData::Shutdown() {
265270
if (flush_tasks_ == nullptr)
266271
return;
@@ -269,21 +274,19 @@ void PerIsolatePlatformData::Shutdown() {
269274
CHECK_NULL(foreground_tasks_.Pop());
270275
CancelPendingDelayedTasks();
271276

277+
ShutdownCbList* copy = new ShutdownCbList(std::move(shutdown_callbacks_));
278+
flush_tasks_->data = copy;
272279
uv_close(reinterpret_cast<uv_handle_t*>(flush_tasks_),
273280
[](uv_handle_t* handle) {
281+
std::unique_ptr<ShutdownCbList> callbacks(
282+
static_cast<ShutdownCbList*>(handle->data));
283+
for (const auto& callback : *callbacks)
284+
callback.cb(callback.data);
274285
delete reinterpret_cast<uv_async_t*>(handle);
275286
});
276287
flush_tasks_ = nullptr;
277288
}
278289

279-
void PerIsolatePlatformData::ref() {
280-
ref_count_++;
281-
}
282-
283-
int PerIsolatePlatformData::unref() {
284-
return --ref_count_;
285-
}
286-
287290
NodePlatform::NodePlatform(int thread_pool_size,
288291
TracingController* tracing_controller) {
289292
if (tracing_controller) {
@@ -298,23 +301,29 @@ NodePlatform::NodePlatform(int thread_pool_size,
298301
void NodePlatform::RegisterIsolate(Isolate* isolate, uv_loop_t* loop) {
299302
Mutex::ScopedLock lock(per_isolate_mutex_);
300303
std::shared_ptr<PerIsolatePlatformData> existing = per_isolate_[isolate];
301-
if (existing) {
302-
CHECK_EQ(loop, existing->event_loop());
303-
existing->ref();
304-
} else {
305-
per_isolate_[isolate] =
306-
std::make_shared<PerIsolatePlatformData>(isolate, loop);
307-
}
304+
CHECK(!existing);
305+
per_isolate_[isolate] =
306+
std::make_shared<PerIsolatePlatformData>(isolate, loop);
308307
}
309308

310309
void NodePlatform::UnregisterIsolate(Isolate* isolate) {
311310
Mutex::ScopedLock lock(per_isolate_mutex_);
312311
std::shared_ptr<PerIsolatePlatformData> existing = per_isolate_[isolate];
313312
CHECK(existing);
314-
if (existing->unref() == 0) {
315-
existing->Shutdown();
316-
per_isolate_.erase(isolate);
313+
existing->Shutdown();
314+
per_isolate_.erase(isolate);
315+
}
316+
317+
void NodePlatform::AddIsolateFinishedCallback(Isolate* isolate,
318+
void (*cb)(void*), void* data) {
319+
Mutex::ScopedLock lock(per_isolate_mutex_);
320+
auto it = per_isolate_.find(isolate);
321+
if (it == per_isolate_.end()) {
322+
CHECK(it->second);
323+
cb(data);
324+
return;
317325
}
326+
it->second->AddShutdownCallback(cb, data);
318327
}
319328

320329
void NodePlatform::Shutdown() {

src/node_platform.h

+10-4
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,9 @@ class PerIsolatePlatformData :
6464
double delay_in_seconds) override;
6565
bool IdleTasksEnabled() override { return false; }
6666

67+
void AddShutdownCallback(void (*callback)(void*), void* data);
6768
void Shutdown();
6869

69-
void ref();
70-
int unref();
71-
7270
// Returns true if work was dispatched or executed. New tasks that are
7371
// posted during flushing of the queue are postponed until the next
7472
// flushing.
@@ -84,7 +82,13 @@ class PerIsolatePlatformData :
8482
static void RunForegroundTask(std::unique_ptr<v8::Task> task);
8583
static void RunForegroundTask(uv_timer_t* timer);
8684

87-
int ref_count_ = 1;
85+
struct ShutdownCallback {
86+
void (*cb)(void*);
87+
void* data;
88+
};
89+
typedef std::vector<ShutdownCallback> ShutdownCbList;
90+
ShutdownCbList shutdown_callbacks_;
91+
8892
uv_loop_t* const loop_;
8993
uv_async_t* flush_tasks_ = nullptr;
9094
TaskQueue<v8::Task> foreground_tasks_;
@@ -145,6 +149,8 @@ class NodePlatform : public MultiIsolatePlatform {
145149

146150
void RegisterIsolate(v8::Isolate* isolate, uv_loop_t* loop) override;
147151
void UnregisterIsolate(v8::Isolate* isolate) override;
152+
void AddIsolateFinishedCallback(v8::Isolate* isolate,
153+
void (*callback)(void*), void* data) override;
148154

149155
std::shared_ptr<v8::TaskRunner> GetForegroundTaskRunner(
150156
v8::Isolate* isolate) override;

src/node_worker.cc

+9-5
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,20 @@ class WorkerThreadData {
188188

189189
w_->platform_->CancelPendingDelayedTasks(isolate);
190190

191+
bool platform_finished = false;
192+
191193
isolate_data_.reset();
194+
195+
w_->platform_->AddIsolateFinishedCallback(isolate, [](void* data) {
196+
*static_cast<bool*>(data) = true;
197+
}, &platform_finished);
192198
w_->platform_->UnregisterIsolate(isolate);
193199

194200
isolate->Dispose();
195201

196-
// Need to run the loop twice more to close the platform's uv_async_t
197-
// TODO(addaleax): It would be better for the platform itself to provide
198-
// some kind of notification when it has fully cleaned up.
199-
uv_run(&loop_, UV_RUN_ONCE);
200-
uv_run(&loop_, UV_RUN_ONCE);
202+
// Wait until the platform has cleaned up all relevant resources.
203+
while (!platform_finished)
204+
uv_run(&loop_, UV_RUN_ONCE);
201205

202206
CheckedUvLoopClose(&loop_);
203207
}

0 commit comments

Comments
 (0)