mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Fix deadlock when recreating websocket connection
Summary: When you reload code while using the Chrome debugger we used to create a new websocket connection before closing the old one. This sometimes would cause a in-flight call to not get a response. This in turn would deadlock the JS thread because we try to shut it down before killing the websocket connection. This change instead makes sure to close the old connection before creating a new one. This is done by using a factory for creating the JavascriptExecutor so we can defer the creation until after the old Bridge has been torn down. public Reviewed By: astreet Differential Revision: D2735011 fb-gh-sync-id: 0ce0f35abaeef5457bad8d6b8d10122281192af4
This commit is contained in:
committed by
facebook-github-bot-5
parent
fc98956b65
commit
6d9096fb3f
@@ -15,6 +15,9 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
@@ -35,14 +38,15 @@ import com.facebook.common.logging.FLog;
|
||||
import com.facebook.infer.annotation.Assertions;
|
||||
import com.facebook.react.R;
|
||||
import com.facebook.react.bridge.CatalystInstance;
|
||||
import com.facebook.react.bridge.JavaJSExecutor;
|
||||
import com.facebook.react.bridge.NativeModuleCallExceptionHandler;
|
||||
import com.facebook.react.bridge.ProxyJavaScriptExecutor;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
import com.facebook.react.bridge.WebsocketJavaScriptExecutor;
|
||||
import com.facebook.react.common.ReactConstants;
|
||||
import com.facebook.react.common.ShakeDetector;
|
||||
import com.facebook.react.common.futures.SimpleSettableFuture;
|
||||
import com.facebook.react.devsupport.StackTraceHelper.StackFrame;
|
||||
import com.facebook.react.modules.debug.DeveloperSettings;
|
||||
|
||||
@@ -534,38 +538,47 @@ public class DevSupportManager implements NativeModuleCallExceptionHandler {
|
||||
// anyway
|
||||
mDevServerHelper.launchChromeDevtools();
|
||||
|
||||
final WebsocketJavaScriptExecutor webSocketJSExecutor = new WebsocketJavaScriptExecutor();
|
||||
webSocketJSExecutor.connect(
|
||||
mDevServerHelper.getWebsocketProxyURL(),
|
||||
new WebsocketJavaScriptExecutor.JSExecutorConnectCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
progressDialog.dismiss();
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mReactInstanceCommandsHandler.onReloadWithJSDebugger(
|
||||
webSocketJSExecutor);
|
||||
}
|
||||
});
|
||||
}
|
||||
JavaJSExecutor.Factory factory = new JavaJSExecutor.Factory() {
|
||||
@Override
|
||||
public JavaJSExecutor create() throws Exception {
|
||||
WebsocketJavaScriptExecutor executor = new WebsocketJavaScriptExecutor();
|
||||
SimpleSettableFuture<Boolean> future = new SimpleSettableFuture<>();
|
||||
executor.connect(
|
||||
mDevServerHelper.getWebsocketProxyURL(),
|
||||
getExecutorConnectCallback(progressDialog, future));
|
||||
// TODO(t9349129) Don't use timeout
|
||||
try {
|
||||
future.get(90, TimeUnit.SECONDS);
|
||||
return executor;
|
||||
} catch (ExecutionException e) {
|
||||
throw (Exception) e.getCause();
|
||||
} catch (InterruptedException | TimeoutException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
mReactInstanceCommandsHandler.onReloadWithJSDebugger(factory);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable cause) {
|
||||
progressDialog.dismiss();
|
||||
FLog.e(ReactConstants.TAG, "Unable to connect to remote debugger", cause);
|
||||
UiThreadUtil.runOnUiThread(
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showNewJavaError(
|
||||
mApplicationContext.getString(R.string.catalyst_remotedbg_error),
|
||||
cause);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
private WebsocketJavaScriptExecutor.JSExecutorConnectCallback getExecutorConnectCallback(
|
||||
final ProgressDialog progressDialog,
|
||||
final SimpleSettableFuture<Boolean> future) {
|
||||
return new WebsocketJavaScriptExecutor.JSExecutorConnectCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
future.set(true);
|
||||
progressDialog.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(final Throwable cause) {
|
||||
progressDialog.dismiss();
|
||||
FLog.e(ReactConstants.TAG, "Unable to connect to remote debugger", cause);
|
||||
future.setException(
|
||||
new IOException(
|
||||
mApplicationContext.getString(R.string.catalyst_remotedbg_error), cause));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void reloadJSFromServer(final ProgressDialog progressDialog) {
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ public interface ReactInstanceDevCommandsHandler {
|
||||
/**
|
||||
* Request react instance recreation with JS debugging enabled.
|
||||
*/
|
||||
void onReloadWithJSDebugger(JavaJSExecutor proxyExecutor);
|
||||
void onReloadWithJSDebugger(JavaJSExecutor.Factory proxyExecutorFactory);
|
||||
|
||||
/**
|
||||
* Notify react instance manager about new JS bundle version downloaded from the server.
|
||||
|
||||
Reference in New Issue
Block a user