Skip to content

Commit b740f53

Browse files
shubhamksavitafacebook-github-bot
authored andcommitted
Add exponential backoff to InspectorPackagerConnection reconnect and exception handling to connect
Summary: ## Summary Fix occasional SIGSEGV in `operator new` during `InspectorPackagerConnection::connectWebSocket()`. ### Root Cause The `InspectorPackagerConnection` reconnection loop retries every 2 seconds with no backoff or limit. When no Metro dev server is reachable, this results in hundreds of failed WebSocket connection attempts over the app lifetime. Each attempt allocates and deallocates C++ hybrid objects, JNI references, and OkHttp WebSocket instances. This accumulated churn leads to heap fragmentation and eventual heap metadata corruption, manifesting as SIGSEGV in `operator new` when the allocator follows corrupted internal pointers. Additionally, the `connect()` method had no exception handling around the `connectWebSocket()` call, meaning any JNI/Java exception would propagate uncaught. ### Changes 1. **Exponential backoff in `reconnect()`**: Start at 2s, double each retry, cap at 120s. This reduces reconnection attempts from ~500 in 17 minutes to ~15, drastically reducing resource churn. 2. **Try-catch in `connect()`**: Catch exceptions from `connectWebSocket()` (e.g., fbjni/JNI exceptions) and gracefully trigger a reconnect instead of crashing. 3. **Reset backoff on success**: When `didOpen()` fires (successful connection), reset the delay back to 2s and clear `suppressConnectionErrors_`. ## Changelog: [General][Fixed] - Add exponential backoff and exception handling to InspectorPackagerConnection reconnect loop to prevent heap fragmentation crashes Reviewed By: cipolleschi Differential Revision: D100956687
1 parent 7128482 commit b740f53

2 files changed

Lines changed: 22 additions & 2 deletions

File tree

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <glog/logging.h>
1515
#include <cerrno>
1616
#include <chrono>
17+
#include <exception>
1718
#include <utility>
1819

1920
using namespace std::literals;
@@ -22,6 +23,7 @@ namespace facebook::react::jsinspector_modern {
2223

2324
static constexpr const std::chrono::duration RECONNECT_DELAY =
2425
std::chrono::milliseconds{2000};
26+
static constexpr const std::chrono::milliseconds MAX_RECONNECT_DELAY{120000};
2527
static constexpr const char* INVALID = "<invalid>";
2628

2729
// InspectorPackagerConnection::Impl method definitions
@@ -301,6 +303,8 @@ void InspectorPackagerConnection::Impl::didReceiveMessage(
301303

302304
void InspectorPackagerConnection::Impl::didOpen() {
303305
connected_ = true;
306+
reconnectDelayMs_ = RECONNECT_DELAY;
307+
suppressConnectionErrors_ = false;
304308
}
305309

306310
void InspectorPackagerConnection::Impl::didClose() {
@@ -333,7 +337,15 @@ void InspectorPackagerConnection::Impl::connect() {
333337
<< "Illegal state: Can't connect after having previously been closed.";
334338
return;
335339
}
336-
webSocket_ = delegate_->connectWebSocket(url_, weak_from_this());
340+
try {
341+
webSocket_ = delegate_->connectWebSocket(url_, weak_from_this());
342+
} catch (const std::exception& e) {
343+
LOG(ERROR) << "Failed to create WebSocket connection: " << e.what();
344+
reconnect();
345+
} catch (...) {
346+
LOG(ERROR) << "Failed to create WebSocket connection: unknown error";
347+
reconnect();
348+
}
337349
}
338350

339351
void InspectorPackagerConnection::Impl::reconnect() {
@@ -357,6 +369,11 @@ void InspectorPackagerConnection::Impl::reconnect() {
357369

358370
reconnectPending_ = true;
359371

372+
auto currentDelay = reconnectDelayMs_;
373+
374+
// Exponential backoff: double the delay for next time, up to the max.
375+
reconnectDelayMs_ = std::min(reconnectDelayMs_ * 2, MAX_RECONNECT_DELAY);
376+
360377
delegate_->scheduleCallback(
361378
[weakSelf = weak_from_this()] {
362379
auto strongSelf = weakSelf.lock();
@@ -370,7 +387,7 @@ void InspectorPackagerConnection::Impl::reconnect() {
370387
strongSelf->connect();
371388
}
372389
},
373-
RECONNECT_DELAY);
390+
currentDelay);
374391
}
375392

376393
void InspectorPackagerConnection::Impl::closeQuietly() {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ class InspectorPackagerConnection::Impl : public IWebSocketDelegate,
132132
// Whether a reconnection is currently pending.
133133
bool reconnectPending_{false};
134134

135+
// Current reconnect delay in milliseconds, used for exponential backoff.
136+
std::chrono::milliseconds reconnectDelayMs_{2000};
137+
135138
SessionId nextSessionId_{1};
136139
};
137140

0 commit comments

Comments
 (0)