Skip to content

Commit 86dbb3b

Browse files
javachemeta-codesync[bot]
authored andcommitted
Fix TSAN data races in InspectorFlags and InspectorPackagerConnection (#56534)
Summary: Pull Request resolved: #56534 Fix two ThreadSanitizer data races caught by the messenger_demo integration test under dev-tsan mode. **InspectorFlags**: `loadFlagsAndAssertUnchanged()` reads and writes `cachedValues_` without synchronization. The main thread calls it via `getIsProfilingBuild()` while the JS thread calls it via `getNetworkInspectionEnabled()`, racing on the shared `optional<Values>`. Add a `std::mutex` to protect `cachedValues_` access. The lock is taken after computing `newValues` from feature flags, so flag reads stay outside the critical section. `dangerouslyResetFlags()` also acquires the lock to avoid racing with concurrent flag reads. **Inspector**: `Inspector::connectDebugger()` is called from the main thread but creates and uses `packagerConnection_` which is also accessed on the inspector thread. Dispatch the body of `connectDebugger()` to the task dispatch thread via `invokeElsePost()`, matching the pattern other Inspector methods already use. This ensures `InspectorPackagerConnection::connect()` is called on the correct thread as its API requires. Changelog: [Internal] Reviewed By: hoxyq Differential Revision: D101809616 fbshipit-source-id: 92eac22ca9ab364daafc403a0fdbb39394bd0f63
1 parent 06b23b8 commit 86dbb3b

9 files changed

Lines changed: 41 additions & 16 deletions

File tree

packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ bool InspectorFlags::getPerfIssuesEnabled() const {
5050
}
5151

5252
void InspectorFlags::dangerouslyResetFlags() {
53-
*this = InspectorFlags{};
53+
std::lock_guard<std::mutex> lock(mutex_);
54+
cachedValues_.reset();
55+
inconsistentFlagsStateLogged_ = false;
56+
fuseboxDisabledForTest_ = false;
5457
}
5558

5659
void InspectorFlags::dangerouslyDisableFuseboxForTest() {
@@ -84,6 +87,8 @@ const InspectorFlags::Values& InspectorFlags::loadFlagsAndAssertUnchanged()
8487
.perfIssuesEnabled = ReactNativeFeatureFlags::perfIssuesEnabled(),
8588
};
8689

90+
// Protect against concurrent calls
91+
std::lock_guard<std::mutex> lock(mutex_);
8792
if (cachedValues_.has_value() && !inconsistentFlagsStateLogged_) {
8893
if (cachedValues_ != newValues) {
8994
LOG(ERROR)
@@ -93,7 +98,6 @@ const InspectorFlags::Values& InspectorFlags::loadFlagsAndAssertUnchanged()
9398
inconsistentFlagsStateLogged_ = true;
9499
}
95100
}
96-
97101
cachedValues_ = newValues;
98102

99103
return cachedValues_.value();

packages/react-native/ReactCommon/jsinspector-modern/InspectorFlags.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#pragma once
99

10+
#include <mutex>
1011
#include <optional>
1112

1213
namespace facebook::react::jsinspector_modern {
@@ -19,6 +20,9 @@ class InspectorFlags {
1920
public:
2021
static InspectorFlags &getInstance();
2122

23+
InspectorFlags(const InspectorFlags &) = delete;
24+
InspectorFlags &operator=(const InspectorFlags &) = delete;
25+
2226
/**
2327
* Flag determining if the inspector backend should strictly assert that only
2428
* a single host is registered.
@@ -81,10 +85,9 @@ class InspectorFlags {
8185
};
8286

8387
InspectorFlags() = default;
84-
InspectorFlags(const InspectorFlags &) = delete;
85-
InspectorFlags &operator=(const InspectorFlags &) = default;
8688
~InspectorFlags() = default;
8789

90+
mutable std::mutex mutex_;
8891
mutable std::optional<Values> cachedValues_;
8992
mutable bool inconsistentFlagsStateLogged_{false};
9093
bool fuseboxDisabledForTest_{false};

packages/react-native/ReactCxxPlatform/react/devsupport/inspector/Inspector.cpp

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,24 @@ Inspector::~Inspector() noexcept {
148148
}
149149

150150
void Inspector::connectDebugger(const std::string& inspectorUrl) noexcept {
151-
if (!packagerConnection_) {
152-
packagerConnection_ =
153-
std::make_unique<jsinspector_modern::InspectorPackagerConnection>(
154-
inspectorUrl,
155-
deviceName_,
156-
appName_,
157-
std::make_unique<InspectorPackagerConnectionDelegate>(
158-
weak_from_this(), webSocketClientFactory_));
159-
}
160-
if (!packagerConnection_->isConnected()) {
161-
packagerConnection_->connect();
162-
}
151+
invokeElsePost([weakThis = weak_from_this(), inspectorUrl]() {
152+
auto self = weakThis.lock();
153+
if (!self) {
154+
return;
155+
}
156+
if (!self->packagerConnection_) {
157+
self->packagerConnection_ =
158+
std::make_unique<jsinspector_modern::InspectorPackagerConnection>(
159+
inspectorUrl,
160+
self->deviceName_,
161+
self->appName_,
162+
std::make_unique<InspectorPackagerConnectionDelegate>(
163+
weakThis, self->webSocketClientFactory_));
164+
}
165+
if (!self->packagerConnection_->isConnected()) {
166+
self->packagerConnection_->connect();
167+
}
168+
});
163169
}
164170

165171
void Inspector::ensureHostTarget(

scripts/cxx-api/api-snapshots/ReactAndroidDebugCxx.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10528,13 +10528,15 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate {
1052810528
}
1052910529

1053010530
class facebook::react::jsinspector_modern::InspectorFlags {
10531+
public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
1053110532
public bool getAssertSingleHostState() const;
1053210533
public bool getFrameRecordingEnabled() const;
1053310534
public bool getFuseboxEnabled() const;
1053410535
public bool getIsProfilingBuild() const;
1053510536
public bool getNetworkInspectionEnabled() const;
1053610537
public bool getPerfIssuesEnabled() const;
1053710538
public bool getScreenshotCaptureEnabled() const;
10539+
public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
1053810540
public static facebook::react::jsinspector_modern::InspectorFlags& getInstance();
1053910541
public void dangerouslyDisableFuseboxForTest();
1054010542
public void dangerouslyResetFlags();

scripts/cxx-api/api-snapshots/ReactAndroidReleaseCxx.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10384,13 +10384,15 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate {
1038410384
}
1038510385

1038610386
class facebook::react::jsinspector_modern::InspectorFlags {
10387+
public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
1038710388
public bool getAssertSingleHostState() const;
1038810389
public bool getFrameRecordingEnabled() const;
1038910390
public bool getFuseboxEnabled() const;
1039010391
public bool getIsProfilingBuild() const;
1039110392
public bool getNetworkInspectionEnabled() const;
1039210393
public bool getPerfIssuesEnabled() const;
1039310394
public bool getScreenshotCaptureEnabled() const;
10395+
public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
1039410396
public static facebook::react::jsinspector_modern::InspectorFlags& getInstance();
1039510397
public void dangerouslyDisableFuseboxForTest();
1039610398
public void dangerouslyResetFlags();

scripts/cxx-api/api-snapshots/ReactAppleDebugCxx.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12809,13 +12809,15 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate {
1280912809
}
1281012810

1281112811
class facebook::react::jsinspector_modern::InspectorFlags {
12812+
public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
1281212813
public bool getAssertSingleHostState() const;
1281312814
public bool getFrameRecordingEnabled() const;
1281412815
public bool getFuseboxEnabled() const;
1281512816
public bool getIsProfilingBuild() const;
1281612817
public bool getNetworkInspectionEnabled() const;
1281712818
public bool getPerfIssuesEnabled() const;
1281812819
public bool getScreenshotCaptureEnabled() const;
12820+
public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
1281912821
public static facebook::react::jsinspector_modern::InspectorFlags& getInstance();
1282012822
public void dangerouslyDisableFuseboxForTest();
1282112823
public void dangerouslyResetFlags();

scripts/cxx-api/api-snapshots/ReactAppleReleaseCxx.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12675,13 +12675,15 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate {
1267512675
}
1267612676

1267712677
class facebook::react::jsinspector_modern::InspectorFlags {
12678+
public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
1267812679
public bool getAssertSingleHostState() const;
1267912680
public bool getFrameRecordingEnabled() const;
1268012681
public bool getFuseboxEnabled() const;
1268112682
public bool getIsProfilingBuild() const;
1268212683
public bool getNetworkInspectionEnabled() const;
1268312684
public bool getPerfIssuesEnabled() const;
1268412685
public bool getScreenshotCaptureEnabled() const;
12686+
public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
1268512687
public static facebook::react::jsinspector_modern::InspectorFlags& getInstance();
1268612688
public void dangerouslyDisableFuseboxForTest();
1268712689
public void dangerouslyResetFlags();

scripts/cxx-api/api-snapshots/ReactCommonDebugCxx.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7636,13 +7636,15 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate {
76367636
}
76377637

76387638
class facebook::react::jsinspector_modern::InspectorFlags {
7639+
public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
76397640
public bool getAssertSingleHostState() const;
76407641
public bool getFrameRecordingEnabled() const;
76417642
public bool getFuseboxEnabled() const;
76427643
public bool getIsProfilingBuild() const;
76437644
public bool getNetworkInspectionEnabled() const;
76447645
public bool getPerfIssuesEnabled() const;
76457646
public bool getScreenshotCaptureEnabled() const;
7647+
public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
76467648
public static facebook::react::jsinspector_modern::InspectorFlags& getInstance();
76477649
public void dangerouslyDisableFuseboxForTest();
76487650
public void dangerouslyResetFlags();

scripts/cxx-api/api-snapshots/ReactCommonReleaseCxx.api

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7627,13 +7627,15 @@ class facebook::react::jsinspector_modern::IWebSocketDelegate {
76277627
}
76287628

76297629
class facebook::react::jsinspector_modern::InspectorFlags {
7630+
public InspectorFlags(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
76307631
public bool getAssertSingleHostState() const;
76317632
public bool getFrameRecordingEnabled() const;
76327633
public bool getFuseboxEnabled() const;
76337634
public bool getIsProfilingBuild() const;
76347635
public bool getNetworkInspectionEnabled() const;
76357636
public bool getPerfIssuesEnabled() const;
76367637
public bool getScreenshotCaptureEnabled() const;
7638+
public facebook::react::jsinspector_modern::InspectorFlags& operator=(const facebook::react::jsinspector_modern::InspectorFlags&) = delete;
76377639
public static facebook::react::jsinspector_modern::InspectorFlags& getInstance();
76387640
public void dangerouslyDisableFuseboxForTest();
76397641
public void dangerouslyResetFlags();

0 commit comments

Comments
 (0)