@@ -120,17 +120,26 @@ static void PostToLoop(const std::shared_ptr<EventLoop>& loop, std::function<voi
120120}
121121
122122void WorkerWrapper::EndWrapperLifetime () {
123- Local<Value> worker =
124- this ->poWorker_ != nullptr ? this ->poWorker_ ->Get (this ->mainIsolate_ ) : Local<Value>();
123+ // The dispatch below runs listeners, and a listener may shut the runtime
124+ // down, whose teardown deletes this wrapper. Everything the dispatch needs is
125+ // read first, and the liveness token says afterwards whether `this` is still
126+ // there to unroot.
127+ Isolate* isolate = this ->mainIsolate_ ;
128+ std::shared_ptr<std::atomic<WorkerWrapper*>> selfRef = this ->selfRef_ ;
129+ Local<Value> worker = this ->poWorker_ != nullptr ? this ->poWorker_ ->Get (isolate) : Local<Value>();
125130 if (!worker.IsEmpty () && worker->IsObject ()) {
126- TryCatch tc (this -> mainIsolate_ );
127- Worker::EmitEnded (this -> mainIsolate_ , worker.As <Object>());
131+ TryCatch tc (isolate );
132+ Worker::EmitEnded (isolate , worker.As <Object>());
128133 if (tc.HasCaught ()) {
129134 Local<Value> error = tc.Exception ();
130- Log (@" %s " , tns::ToString (this -> mainIsolate_ , error).c_str ());
131- this -> mainIsolate_ ->ThrowException (error);
135+ Log (@" %s " , tns::ToString (isolate , error).c_str ());
136+ isolate ->ThrowException (error);
132137 }
133138 }
139+ if (selfRef->load (std::memory_order_acquire) == nullptr ) {
140+ // Deleted during the dispatch; that teardown released the Worker object.
141+ return ;
142+ }
134143 this ->UnrootWorkerObject ();
135144}
136145
@@ -181,7 +190,7 @@ static void PostToLoop(const std::shared_ptr<EventLoop>& loop, std::function<voi
181190 this ->onMessage_ (this ->workerIsolate_ , globalTarget, message);
182191
183192 if (tc.HasCaught ()) {
184- this ->CallOnErrorHandlers (tc);
193+ this ->CallOnErrorHandlers (this -> workerIsolate_ , tc);
185194 }
186195 }
187196
@@ -232,7 +241,11 @@ static void PostThreadEndedNotification(Isolate* mainIsolate, std::weak_ptr<Even
232241 },
233242 this );
234243
235- this ->workerIsolate_ = func ();
244+ Isolate* workerIsolate = func ();
245+ {
246+ std::lock_guard<std::mutex> lock (this ->workerIsolateMutex_ );
247+ this ->workerIsolate_ = workerIsolate;
248+ }
236249
237250 this ->DrainPendingTasks ();
238251
@@ -242,6 +255,14 @@ static void PostThreadEndedNotification(Isolate* mainIsolate, std::weak_ptr<Even
242255 }
243256 }
244257
258+ // Withdrawn before the runtime and its isolate go away below. Terminate()
259+ // uses the isolate under this mutex, so a terminate that already read it has
260+ // finished with it by the time this returns, and a later one finds null.
261+ {
262+ std::lock_guard<std::mutex> lock (this ->workerIsolateMutex_ );
263+ this ->workerIsolate_ = nullptr ;
264+ }
265+
245266 // The inspector must be gone before the Runtime (and with it the isolate)
246267 // is deleted below.
247268 this ->DestroyInspector ();
@@ -292,6 +313,9 @@ static void PostThreadEndedNotification(Isolate* mainIsolate, std::weak_ptr<Even
292313 // set terminating to true atomically
293314 bool wasTerminating = this ->isTerminating_ .exchange (true );
294315 if (!wasTerminating) {
316+ // Held across the use, not just the read: the worker thread withdraws the
317+ // isolate under the same mutex before deleting its runtime.
318+ std::unique_lock<std::mutex> isolateLock (this ->workerIsolateMutex_ );
295319 if (this ->workerIsolate_ != nullptr ) {
296320 // Flagged before the request so a pump that is between iterations sees
297321 // it on its next check, rather than only once V8 has some JS to
@@ -307,6 +331,7 @@ static void PostThreadEndedNotification(Isolate* mainIsolate, std::weak_ptr<Even
307331 }
308332 this ->workerIsolate_ ->TerminateExecution ();
309333 }
334+ isolateLock.unlock ();
310335 {
311336 // A worker paused at a breakpoint sits in the inspector's nested pause
312337 // loop, not in the CFRunLoop — kick it loose so TerminateExecution and
@@ -414,11 +439,10 @@ static void PostThreadEndedNotification(Isolate* mainIsolate, std::weak_ptr<Even
414439 delete client;
415440}
416441
417- void WorkerWrapper::CallOnErrorHandlers (TryCatch& tc) {
442+ void WorkerWrapper::CallOnErrorHandlers (Isolate* isolate, TryCatch& tc) {
418443 if (this ->isTerminating_ ) {
419444 return ;
420445 }
421- Isolate* isolate = this ->workerIsolate_ ;
422446 Local<Context> context = Caches::Get (isolate)->GetContext ();
423447 Local<Object> global = context->Global ();
424448
@@ -447,11 +471,11 @@ static void PostThreadEndedNotification(Isolate* mainIsolate, std::weak_ptr<Even
447471 this ->PassUncaughtExceptionFromWorkerToMain (context, tc);
448472}
449473
450- void WorkerWrapper::ReportEntryEvaluationRejection (Local<Context> context, Local<Value> reason) {
474+ void WorkerWrapper::ReportEntryEvaluationRejection (Isolate* isolate, Local<Context> context,
475+ Local<Value> reason) {
451476 if (this ->isTerminating_ ) {
452477 return ;
453478 }
454- Isolate* isolate = this ->workerIsolate_ ;
455479 Local<Object> global = context->Global ();
456480
457481 Local<Value> onErrorVal;
0 commit comments