Skip to content

feat(inspector): attach Chrome DevTools to Web Worker isolates#1973

Open
edusperoni wants to merge 1 commit into
mainfrom
feat/worker-inspector
Open

feat(inspector): attach Chrome DevTools to Web Worker isolates#1973
edusperoni wants to merge 1 commit into
mainfrom
feat/worker-inspector

Conversation

@edusperoni

@edusperoni edusperoni commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Workers were invisible to the debugger: the inspector only served the main isolate and even dropped worker console output on the floor. DevTools discovers extra isolates through the Target domain with flat-session multiplexing, so implement that surface in the runtime:

  • Add WorkerInspectorClient: a per-worker V8Inspector + session that lives entirely on the worker's thread. Incoming CDP messages queue through an eventfd on the worker's ALooper (the same mechanism as the worker message inbox); while paused at a breakpoint a nested loop on the worker thread pumps the same queue without re-entering the looper, so postMessage deliveries stay queued during a pause, matching Chrome's semantics.
  • Turn JsV8InspectorClient into the root session + router: handle Target.setAutoAttach natively on the socket thread (announce workers via Target.attachedToTarget, detach on death), route messages carrying a top-level sessionId to the worker's thread directly from the socket thread, and make the frontend sender thread-safe. Routing off the socket thread means a worker stays debuggable while the main isolate is paused, and vice versa.
  • Debugger.pause for a busy worker uses RequestInterrupt keyed by workerId, so a late-firing interrupt after worker death is a no-op; it is skipped while that worker is already paused, avoiding a spurious re-pause after resume (same guard as the main isolate).
  • Serve Network.loadNetworkResource and IO.read/IO.close on the socket thread for any session (sessionId echoed), so worker source maps load too, and apply the nsruntime:// sourceMapURL rewrite to worker Debugger.scriptParsed events.
  • Route worker console.* to the worker's own inspector and deliver through V8ConsoleMessageStorage::addMessage instead of the live-only runtime agent path: messages logged before the frontend attaches are stored and replayed as console history. Applies to the main isolate as well.
  • Name execution contexts ("main" / worker script url) so the DevTools console context selector rows are labeled and selectable.
  • Worker lifecycle: the inspector is created on the worker thread before the script runs (debug builds only), terminate() kicks a paused worker out of its nested pause loop, teardown unregisters the target before the isolate is disposed, and frontend reconnects reset worker sessions (ordered before the main-isolate Locker so workers recover even if the main isolate is paused).
  • Make isConnected_ atomic: worker threads now read it while the main thread writes the adjacent bitfield flags.

waitForDebuggerOnStart (pause new workers before their first line) is left as future work; workers announce waitingForDebugger: false.

Ports NativeScript/ios#386 to Android.

Summary by CodeRabbit

  • New Features

    • Enhanced debugger support for worker threads accessible via Chrome DevTools
    • Worker console messages now visible in DevTools inspector
  • Bug Fixes

    • Improved thread-safety for concurrent inspector message handling
    • Enhanced protocol message routing and session management

Workers were invisible to the debugger: the inspector only served the
main isolate and even dropped worker console output on the floor.
DevTools discovers extra isolates through the Target domain with
flat-session multiplexing, so implement that surface in the runtime:

- Add WorkerInspectorClient: a per-worker V8Inspector + session that
  lives entirely on the worker's thread. Incoming CDP messages queue
  through an eventfd on the worker's ALooper (the same mechanism as the
  worker message inbox); while paused at a breakpoint a nested loop on
  the worker thread pumps the same queue without re-entering the
  looper, so postMessage deliveries stay queued during a pause,
  matching Chrome's semantics.
- Turn JsV8InspectorClient into the root session + router: handle
  Target.setAutoAttach natively on the socket thread (announce workers
  via Target.attachedToTarget, detach on death), route messages
  carrying a top-level sessionId to the worker's thread directly from
  the socket thread, and make the frontend sender thread-safe. Routing
  off the socket thread means a worker stays debuggable while the main
  isolate is paused, and vice versa.
- Debugger.pause for a busy worker uses RequestInterrupt keyed by
  workerId, so a late-firing interrupt after worker death is a no-op;
  it is skipped while that worker is already paused, avoiding a
  spurious re-pause after resume (same guard as the main isolate).
- Serve Network.loadNetworkResource and IO.read/IO.close on the socket
  thread for any session (sessionId echoed), so worker source maps
  load too, and apply the nsruntime:// sourceMapURL rewrite to worker
  Debugger.scriptParsed events.
- Route worker console.* to the worker's own inspector and deliver
  through V8ConsoleMessageStorage::addMessage instead of the live-only
  runtime agent path: messages logged before the frontend attaches are
  stored and replayed as console history. Applies to the main isolate
  as well.
- Name execution contexts ("main" / worker script url) so the DevTools
  console context selector rows are labeled and selectable.
- Worker lifecycle: the inspector is created on the worker thread
  before the script runs (debug builds only), terminate() kicks a
  paused worker out of its nested pause loop, teardown unregisters the
  target before the isolate is disposed, and frontend reconnects reset
  worker sessions (ordered before the main-isolate Locker so workers
  recover even if the main isolate is paused).
- Make isConnected_ atomic: worker threads now read it while the main
  thread writes the adjacent bitfield flags.

waitForDebuggerOnStart (pause new workers before their first line) is
left as future work; workers announce waitingForDebugger: false.

Ports NativeScript/ios#386 to Android.
@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds worker thread debugging support to the Android V8 inspector. The socket message handler contract shifts from string return to boolean-with-response, worker targets are registered and routed via session identifiers, a new per-worker inspector client integrates with the worker's event loop, and inspector lifecycle is wired into worker bootstrap/teardown with console message forwarding.

Changes

Worker Thread Debugging Infrastructure

Layer / File(s) Summary
Socket Message Handler Contract and Java Bridge
test-app/app/src/main/java/com/tns/AndroidJsV8Inspector.java, test-app/runtime/src/main/cpp/com_tns_AndroidJsV8Inspector.cpp, test-app/runtime/CMakeLists.txt
Java fast-path refined to send only non-empty responses. JNI bridge and CMakeLists updated to use new bool handleMessageOnSocketThread(message, response) contract.
Main Inspector Thread Safety and Includes
test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp (includes, helpers)
Singleton changed to std::atomic<JsV8InspectorClient*>, includes added for worker/string utilities, and file-local helpers adjusted.
Connection Lifecycle and Disconnect Refactor
test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp (disconnect)
disconnect() resets worker sessions first, synchronizes JNI cleanup under connectionMutex_, and recreates inspector session.
Worker Target Routing and Registration
test-app/runtime/src/main/cpp/JsV8InspectorClient.h, test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp (routing)
Introduces WorkerTarget struct and worker tracking map. Socket handler routes by sessionId, handles Target.setAutoAttach and Debugger.pause with pause-loop awareness, and provides registration/announcement helpers.
Frontend Communication and Outbound Protocol
test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp (SendToFrontend, callbacks)
New SendToFrontend snapshots JNI refs under connectionMutex_ and clears exceptions. Singleton and callbacks refactored for atomic semantics and proper connection state checking.
Connection State and Console Logging
test-app/runtime/src/main/cpp/JsV8InspectorClient.h, test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp (atomic state, console)
Connection state upgraded to std::atomic<bool>. Console logging routes workers to WorkerWrapper and main-thread to V8 inspector message storage replay.
Worker Inspector Client Contract
test-app/runtime/src/main/cpp/WorkerInspectorClient.h
Declares WorkerInspectorClient implementing V8InspectorClient and V8Inspector::Channel. Exposes cross-thread messaging, pause control, termination, logging, and session reset with required member fields.
Worker Inspector Client Implementation
test-app/runtime/src/main/cpp/WorkerInspectorClient.cpp
Implements worker-side inspector with eventfd-backed looper integration, message queueing, protocol dispatch, pause-loop message pump, interrupt scheduling, flat-session protocol wrapping, and V8 stack-trace-based console logging.
WorkerWrapper Inspector Lifecycle Integration
test-app/runtime/src/main/cpp/WorkerWrapper.h, test-app/runtime/src/main/cpp/WorkerWrapper.cpp
Adds debug-only inspector creation during bootstrap with file-based URL, termination notification under lock, destruction before runtime teardown, and console-message forwarding to active inspector.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • NativeScript/android#1967: Refines the WebSocket fast-path non-empty-vs-null semantics for handleMessageOnSocketThread(...).
  • NativeScript/android#1969: Adds initial Network.loadNetworkResource/IO.read/IO.close handling to the socket-thread message handler prior to the bool+response contract refactor in this PR.

Suggested reviewers

  • rigor789
  • NathanWalker

🐰 Worker threads now share the debugging dream,
DevTools sees each isolate's shimmering stream,
Flat sessions, pause loops, and messages flow free,
Console logs bloom where the workers decree!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.82% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main objective of the PR: adding Chrome DevTools support for Web Worker isolates through the new WorkerInspectorClient implementation.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp (1)

514-524: 🩺 Stability & Availability | 🏗️ Heavy lift

Avoid socket writes while holding workerTargetsMutex_.

Line 514+ / Line 542+ / Line 571+ / Line 585+ call SendToFrontend(...) while the registry mutex is held. If socket send blocks, worker register/unregister/routing can stall behind that lock. Snapshot payloads under lock, then send after unlocking.

Also applies to: 542-559, 571-577, 585-605

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp` around lines 514 -
524, You're holding workerTargetsMutex_ while performing socket writes via
SendToFrontend/JsonDump (e.g., in the branch handling missing sessionId and
other branches that currently call SendToFrontend while locked), which can block
other registry operations; instead, inside the critical section (while holding
workerTargetsMutex_) construct and copy the JSON payload/string you intend to
send and then release the lock, and only after unlocking call
SendToFrontend(JsonDump(...)) or SendToFrontend(copiedPayload). Apply this
pattern for all occurrences that call SendToFrontend while workerTargetsMutex_
is held (search for SendToFrontend usages near workerTargets_ handling: the
missing-session branch, and the other locations noted in the comment).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp`:
- Around line 814-821: GetInstance currently does an atomic load->new->store
which can race and leak if two threads concurrently see null; change
JsV8InspectorClient::GetInstance to perform a race-free singleton init by using
either std::call_once with a std::once_flag or an atomic compare_exchange loop
on the static instance so only one caller constructs the object and others reuse
it; keep the creation using Runtime::GetRuntime(0)->GetIsolate() and ensure
GetInstanceIfCreated remains unchanged, and verify callers like
Java_com_tns_AndroidJsV8Inspector_handleMessageOnSocketThread use the fixed
GetInstance to avoid double construction.

In `@test-app/runtime/src/main/cpp/WorkerInspectorClient.cpp`:
- Around line 205-239: The pauseTerminated_ flag is subject to a data race
because it is written from NotifyTerminating() (called from another thread)
while being read/modified in runMessageLoopOnPause() and
quitMessageLoopOnPause(); change the pauseTerminated_ member in
WorkerInspectorClient to std::atomic<bool> and update all accesses to use atomic
operations with appropriate memory ordering (e.g., load(memory_order_acquire)
when reading in runMessageLoopOnPause and store(memory_order_release) when
setting in NotifyTerminating and quitMessageLoopOnPause) so reads/writes are
synchronized across threads.

---

Nitpick comments:
In `@test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp`:
- Around line 514-524: You're holding workerTargetsMutex_ while performing
socket writes via SendToFrontend/JsonDump (e.g., in the branch handling missing
sessionId and other branches that currently call SendToFrontend while locked),
which can block other registry operations; instead, inside the critical section
(while holding workerTargetsMutex_) construct and copy the JSON payload/string
you intend to send and then release the lock, and only after unlocking call
SendToFrontend(JsonDump(...)) or SendToFrontend(copiedPayload). Apply this
pattern for all occurrences that call SendToFrontend while workerTargetsMutex_
is held (search for SendToFrontend usages near workerTargets_ handling: the
missing-session branch, and the other locations noted in the comment).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a62cec75-6809-44d1-af66-cbcd3dff76e0

📥 Commits

Reviewing files that changed from the base of the PR and between a84d3c7 and 2656b65.

📒 Files selected for processing (9)
  • test-app/app/src/main/java/com/tns/AndroidJsV8Inspector.java
  • test-app/runtime/CMakeLists.txt
  • test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp
  • test-app/runtime/src/main/cpp/JsV8InspectorClient.h
  • test-app/runtime/src/main/cpp/WorkerInspectorClient.cpp
  • test-app/runtime/src/main/cpp/WorkerInspectorClient.h
  • test-app/runtime/src/main/cpp/WorkerWrapper.cpp
  • test-app/runtime/src/main/cpp/WorkerWrapper.h
  • test-app/runtime/src/main/cpp/com_tns_AndroidJsV8Inspector.cpp

Comment on lines 814 to 821
JsV8InspectorClient* JsV8InspectorClient::GetInstance() {
if (instance == nullptr) {
instance = new JsV8InspectorClient(Runtime::GetRuntime(0)->GetIsolate());
JsV8InspectorClient* client = instance.load(std::memory_order_acquire);
if (client == nullptr) {
// Main-thread entry points only (JNI init/connect/dispatch); worker
// threads use GetInstanceIfCreated and never construct.
client = new JsV8InspectorClient(Runtime::GetRuntime(0)->GetIsolate());
instance.store(client, std::memory_order_release);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify singleton initialization pattern and whether CAS/call_once is absent/present.
rg -n "JsV8InspectorClient::GetInstance\\(|instance\\.load|instance\\.store|compare_exchange|call_once" --type=cpp

Repository: NativeScript/android

Length of output: 3151


🏁 Script executed:

#!/bin/bash
sed -n '780,860p' test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp | cat -n

Repository: NativeScript/android

Length of output: 3520


🏁 Script executed:

#!/bin/bash
# Find declaration/type of `instance`
rg -n "instance\s*;" test-app/runtime/src/main/cpp/JsV8InspectorClient.* || true
rg -n "static.*instance|std::atomic.*instance|atomic<.*>.*instance|instance\s*\(" test-app/runtime/src/main/cpp/JsV8InspectorClient.* || true

Repository: NativeScript/android

Length of output: 425


🏁 Script executed:

#!/bin/bash
rg -n "JsV8InspectorClient::GetInstance\\(" --type=cpp test-app/runtime/src/main/cpp

Repository: NativeScript/android

Length of output: 1343


🏁 Script executed:

#!/bin/bash
sed -n '1,120p' test-app/runtime/src/main/cpp/com_tns_AndroidJsV8Inspector.cpp | cat -n

Repository: NativeScript/android

Length of output: 2226


🏁 Script executed:

#!/bin/bash
rg -n "handleMessageOnSocketThread\\(" test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp

Repository: NativeScript/android

Length of output: 175


🏁 Script executed:

#!/bin/bash
sed -n '380,520p' test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp | cat -n

Repository: NativeScript/android

Length of output: 7449


Make JsV8InspectorClient singleton initialization race-free.

JsV8InspectorClient::GetInstance() still does an atomic load -> new -> store, so two concurrent first callers can both construct and one instance can be leaked/overwritten. The “main-thread entry points only; worker threads use GetInstanceIfCreated” assumption doesn’t hold because Java_com_tns_AndroidJsV8Inspector_handleMessageOnSocketThread calls GetInstance() (not GetInstanceIfCreated()).

Proposed fix
 JsV8InspectorClient* JsV8InspectorClient::GetInstance() {
-    JsV8InspectorClient* client = instance.load(std::memory_order_acquire);
-    if (client == nullptr) {
-        // Main-thread entry points only (JNI init/connect/dispatch); worker
-        // threads use GetInstanceIfCreated and never construct.
-        client = new JsV8InspectorClient(Runtime::GetRuntime(0)->GetIsolate());
-        instance.store(client, std::memory_order_release);
-    }
-
-    return client;
+    JsV8InspectorClient* client = instance.load(std::memory_order_acquire);
+    if (client != nullptr) {
+        return client;
+    }
+
+    auto* created = new JsV8InspectorClient(Runtime::GetRuntime(0)->GetIsolate());
+    JsV8InspectorClient* expected = nullptr;
+    if (!instance.compare_exchange_strong(
+            expected, created, std::memory_order_release, std::memory_order_acquire)) {
+        delete created;
+        return expected;
+    }
+    return created;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test-app/runtime/src/main/cpp/JsV8InspectorClient.cpp` around lines 814 -
821, GetInstance currently does an atomic load->new->store which can race and
leak if two threads concurrently see null; change
JsV8InspectorClient::GetInstance to perform a race-free singleton init by using
either std::call_once with a std::once_flag or an atomic compare_exchange loop
on the static instance so only one caller constructs the object and others reuse
it; keep the creation using Runtime::GetRuntime(0)->GetIsolate() and ensure
GetInstanceIfCreated remains unchanged, and verify callers like
Java_com_tns_AndroidJsV8Inspector_handleMessageOnSocketThread use the fixed
GetInstance to avoid double construction.

Comment on lines +205 to +239
void WorkerInspectorClient::runMessageLoopOnPause(int contextGroupId) {
if (runningPauseLoop_.load(std::memory_order_acquire) || dying_) {
return;
}
runningPauseLoop_.store(true, std::memory_order_release);
pauseTerminated_ = false;

while (!pauseTerminated_ && !dying_) {
std::string message = this->PopMessage();
bool shouldWait = message.empty();
if (!shouldWait) {
this->DispatchOne(message);
}

while (v8::platform::PumpMessageLoop(Runtime::platform, isolate_)) {
}

if (shouldWait && !pauseTerminated_ && !dying_) {
std::unique_lock<std::mutex> lock(messageArrivedMutex_);
messageArrived_.wait_for(lock, std::chrono::milliseconds(1));
}
}

runningPauseLoop_.store(false, std::memory_order_release);
}

void WorkerInspectorClient::quitMessageLoopOnPause() {
pauseTerminated_ = true;
}

void WorkerInspectorClient::NotifyTerminating() {
dying_ = true;
pauseTerminated_ = true;
messageArrived_.notify_all();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Data race on pauseTerminated_ flag.

pauseTerminated_ is written from a different thread in NotifyTerminating() (called via WorkerWrapper::Terminate() from the parent thread) while it may be read concurrently in runMessageLoopOnPause() on the worker thread. This needs to be an std::atomic<bool> with appropriate memory ordering.

🔒 Proposed fix: make pauseTerminated_ atomic

In the header, change:

- bool pauseTerminated_ = false;
+ std::atomic<bool> pauseTerminated_{false};

Then update usages with appropriate memory ordering:

 void WorkerInspectorClient::runMessageLoopOnPause(int contextGroupId) {
     if (runningPauseLoop_.load(std::memory_order_acquire) || dying_) {
         return;
     }
     runningPauseLoop_.store(true, std::memory_order_release);
-    pauseTerminated_ = false;
+    pauseTerminated_.store(false, std::memory_order_relaxed);
 
-    while (!pauseTerminated_ && !dying_) {
+    while (!pauseTerminated_.load(std::memory_order_acquire) && !dying_) {
         // ...
-        if (shouldWait && !pauseTerminated_ && !dying_) {
+        if (shouldWait && !pauseTerminated_.load(std::memory_order_acquire) && !dying_) {
             // ...
         }
     }
     // ...
 }
 
 void WorkerInspectorClient::quitMessageLoopOnPause() {
-    pauseTerminated_ = true;
+    pauseTerminated_.store(true, std::memory_order_release);
 }
 
 void WorkerInspectorClient::NotifyTerminating() {
     dying_ = true;
-    pauseTerminated_ = true;
+    pauseTerminated_.store(true, std::memory_order_release);
     messageArrived_.notify_all();
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
void WorkerInspectorClient::runMessageLoopOnPause(int contextGroupId) {
if (runningPauseLoop_.load(std::memory_order_acquire) || dying_) {
return;
}
runningPauseLoop_.store(true, std::memory_order_release);
pauseTerminated_ = false;
while (!pauseTerminated_ && !dying_) {
std::string message = this->PopMessage();
bool shouldWait = message.empty();
if (!shouldWait) {
this->DispatchOne(message);
}
while (v8::platform::PumpMessageLoop(Runtime::platform, isolate_)) {
}
if (shouldWait && !pauseTerminated_ && !dying_) {
std::unique_lock<std::mutex> lock(messageArrivedMutex_);
messageArrived_.wait_for(lock, std::chrono::milliseconds(1));
}
}
runningPauseLoop_.store(false, std::memory_order_release);
}
void WorkerInspectorClient::quitMessageLoopOnPause() {
pauseTerminated_ = true;
}
void WorkerInspectorClient::NotifyTerminating() {
dying_ = true;
pauseTerminated_ = true;
messageArrived_.notify_all();
}
void WorkerInspectorClient::runMessageLoopOnPause(int contextGroupId) {
if (runningPauseLoop_.load(std::memory_order_acquire) || dying_) {
return;
}
runningPauseLoop_.store(true, std::memory_order_release);
pauseTerminated_.store(false, std::memory_order_relaxed);
while (!pauseTerminated_.load(std::memory_order_acquire) && !dying_) {
std::string message = this->PopMessage();
bool shouldWait = message.empty();
if (!shouldWait) {
this->DispatchOne(message);
}
while (v8::platform::PumpMessageLoop(Runtime::platform, isolate_)) {
}
if (shouldWait && !pauseTerminated_.load(std::memory_order_acquire) && !dying_) {
std::unique_lock<std::mutex> lock(messageArrivedMutex_);
messageArrived_.wait_for(lock, std::chrono::milliseconds(1));
}
}
runningPauseLoop_.store(false, std::memory_order_release);
}
void WorkerInspectorClient::quitMessageLoopOnPause() {
pauseTerminated_.store(true, std::memory_order_release);
}
void WorkerInspectorClient::NotifyTerminating() {
dying_ = true;
pauseTerminated_.store(true, std::memory_order_release);
messageArrived_.notify_all();
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test-app/runtime/src/main/cpp/WorkerInspectorClient.cpp` around lines 205 -
239, The pauseTerminated_ flag is subject to a data race because it is written
from NotifyTerminating() (called from another thread) while being read/modified
in runMessageLoopOnPause() and quitMessageLoopOnPause(); change the
pauseTerminated_ member in WorkerInspectorClient to std::atomic<bool> and update
all accesses to use atomic operations with appropriate memory ordering (e.g.,
load(memory_order_acquire) when reading in runMessageLoopOnPause and
store(memory_order_release) when setting in NotifyTerminating and
quitMessageLoopOnPause) so reads/writes are synchronized across threads.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant