Skip to content

Commit afba46a

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.
1 parent 97ce5e0 commit afba46a

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
@@ -81,8 +81,6 @@ IsolateData::IsolateData(Isolate* isolate,
8181
uses_node_allocator_(allocator_ == node_allocator_),
8282
platform_(platform) {
8383
CHECK_NOT_NULL(allocator_);
84-
if (platform_ != nullptr)
85-
platform_->RegisterIsolate(isolate_, event_loop);
8684

8785
options_.reset(
8886
new PerIsolateOptions(*(per_process::cli_options->per_isolate)));
@@ -134,12 +132,6 @@ IsolateData::IsolateData(Isolate* isolate,
134132
#undef V
135133
}
136134

137-
IsolateData::~IsolateData() {
138-
if (platform_ != nullptr)
139-
platform_->UnregisterIsolate(isolate_);
140-
}
141-
142-
143135
void InitThreadLocalOnce() {
144136
CHECK_EQ(0, uv_key_create(&Environment::thread_local_env));
145137
}

src/env.h

-1
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,6 @@ class IsolateData {
399399
uv_loop_t* event_loop,
400400
MultiIsolatePlatform* platform = nullptr,
401401
ArrayBufferAllocator* node_allocator = nullptr);
402-
~IsolateData();
403402
inline uv_loop_t* event_loop() const;
404403
inline MultiIsolatePlatform* platform() const;
405404
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
@@ -260,6 +260,11 @@ PerIsolatePlatformData::~PerIsolatePlatformData() {
260260
Shutdown();
261261
}
262262

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

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

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

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

319328
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
@@ -186,16 +186,20 @@ class WorkerThreadData {
186186

187187
w_->platform_->CancelPendingDelayedTasks(isolate);
188188

189+
bool platform_finished = false;
190+
189191
isolate_data_.reset();
192+
193+
w_->platform_->AddIsolateFinishedCallback(isolate, [](void* data) {
194+
*static_cast<bool*>(data) = true;
195+
}, &platform_finished);
190196
w_->platform_->UnregisterIsolate(isolate);
191197

192198
isolate->Dispose();
193199

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

200204
CheckedUvLoopClose(&loop_);
201205
}

0 commit comments

Comments
 (0)