Refactor so that mBridge can not be null and can be final

Reviewed By: astreet

Differential Revision: D2717983

fb-gh-sync-id: 969499eb062d54e0271f910c06c4539e2cb20030
This commit is contained in:
Alexander Blom 2015-12-12 14:19:16 -08:00 committed by facebook-github-bot-7
parent f2d84456e5
commit 8de172e92d
4 changed files with 122 additions and 56 deletions

View File

@ -14,6 +14,7 @@ import javax.annotation.Nullable;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -63,7 +64,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
private boolean mInitialized = false;
// Access from JS thread
private @Nullable ReactBridge mBridge;
private final ReactBridge mBridge;
private boolean mJSBundleHasLoaded;
private CatalystInstanceImpl(
@ -83,39 +84,34 @@ public class CatalystInstanceImpl implements CatalystInstance {
mNativeModuleCallExceptionHandler = nativeModuleCallExceptionHandler;
mTraceListener = new JSProfilerTraceListener();
final CountDownLatch initLatch = new CountDownLatch(1);
mCatalystQueueConfiguration.getJSQueueThread().runOnQueue(
new Runnable() {
@Override
public void run() {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge");
try {
initializeBridge(jsExecutor, jsModulesConfig);
initLatch.countDown();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
});
try {
Assertions.assertCondition(
initLatch.await(BRIDGE_SETUP_TIMEOUT_MS, TimeUnit.MILLISECONDS),
"Timed out waiting for bridge to initialize!");
} catch (InterruptedException e) {
throw new RuntimeException(e);
mBridge = mCatalystQueueConfiguration.getJSQueueThread().callOnQueue(
new Callable<ReactBridge>() {
@Override
public ReactBridge call() throws Exception {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "initializeBridge");
try {
return initializeBridge(jsExecutor, jsModulesConfig);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}).get(BRIDGE_SETUP_TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (Exception t) {
throw new RuntimeException("Failed to initialize bridge", t);
}
}
private void initializeBridge(
private ReactBridge initializeBridge(
JavaScriptExecutor jsExecutor,
JavaScriptModulesConfig jsModulesConfig) {
mCatalystQueueConfiguration.getJSQueueThread().assertIsOnThread();
Assertions.assertCondition(mBridge == null, "initializeBridge should be called once");
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "ReactBridgeCtor");
ReactBridge bridge;
try {
mBridge = new ReactBridge(
bridge = new ReactBridge(
jsExecutor,
new NativeModulesReactCallback(),
mCatalystQueueConfiguration.getNativeModulesQueueThread());
@ -125,15 +121,17 @@ public class CatalystInstanceImpl implements CatalystInstance {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "setBatchedBridgeConfig");
try {
mBridge.setGlobalVariable(
bridge.setGlobalVariable(
"__fbBatchedBridgeConfig",
buildModulesConfigJSONProperty(mJavaRegistry, jsModulesConfig));
mBridge.setGlobalVariable(
bridge.setGlobalVariable(
"__RCTProfileIsProfiling",
Systrace.isTracing(Systrace.TRACE_TAG_REACT_APPS) ? "true" : "false");
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
return bridge;
}
@Override
@ -260,13 +258,11 @@ public class CatalystInstanceImpl implements CatalystInstance {
}
}
if (mBridge != null) {
Systrace.unregisterListener(mTraceListener);
}
Systrace.unregisterListener(mTraceListener);
// We can access the Bridge from any thread now because we know either we are on the JS thread
// or the JS thread has finished via CatalystQueueConfiguration#destroy()
Assertions.assertNotNull(mBridge).dispose();
mBridge.dispose();
}
@Override
@ -294,8 +290,7 @@ public class CatalystInstanceImpl implements CatalystInstance {
}
@VisibleForTesting
public @Nullable
ReactBridge getBridge() {
public ReactBridge getBridge() {
return mBridge;
}
@ -341,25 +336,16 @@ public class CatalystInstanceImpl implements CatalystInstance {
@Override
public boolean supportsProfiling() {
if (mBridge == null) {
return false;
}
return mBridge.supportsProfiling();
}
@Override
public void startProfiler(String title) {
if (mBridge == null) {
return;
}
mBridge.startProfiler(title);
}
@Override
public void stopProfiler(String title, String filename) {
if (mBridge == null) {
return;
}
mBridge.stopProfiler(title, filename);
}

View File

@ -9,6 +9,9 @@
package com.facebook.react.bridge.queue;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import com.facebook.proguard.annotations.DoNotStrip;
/**
@ -23,6 +26,13 @@ public interface MessageQueueThread {
@DoNotStrip
void runOnQueue(Runnable runnable);
/**
* Runs the given Callable on this Thread. It will be submitted to the end of the event queue even
* if it is being submitted from the same queue Thread.
*/
@DoNotStrip
<T> Future<T> callOnQueue(final Callable<T> callable);
/**
* @return whether the current Thread is also the Thread associated with this MessageQueueThread.
*/

View File

@ -9,6 +9,10 @@
package com.facebook.react.bridge.queue;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import android.os.Looper;
import com.facebook.common.logging.FLog;
@ -45,6 +49,7 @@ import com.facebook.react.common.futures.SimpleSettableFuture;
* if it is being submitted from the same queue Thread.
*/
@DoNotStrip
@Override
public void runOnQueue(Runnable runnable) {
if (mIsFinished) {
FLog.w(
@ -55,9 +60,29 @@ import com.facebook.react.common.futures.SimpleSettableFuture;
mHandler.post(runnable);
}
@DoNotStrip
@Override
public <T> Future<T> callOnQueue(final Callable<T> callable) {
final SimpleSettableFuture<T> future = new SimpleSettableFuture<>();
runOnQueue(
new Runnable() {
@Override
public void run() {
try {
future.set(callable.call());
} catch (Exception e) {
future.setException(e);
}
}
});
return future;
}
/**
* @return whether the current Thread is also the Thread associated with this MessageQueueThread.
*/
@Override
public boolean isOnThread() {
return mLooper.getThread() == Thread.currentThread();
}
@ -66,6 +91,7 @@ import com.facebook.react.common.futures.SimpleSettableFuture;
* Asserts {@link #isOnThread()}, throwing a {@link AssertionException} (NOT an
* {@link AssertionError}) if the assertion fails.
*/
@Override
public void assertIsOnThread() {
SoftAssertions.assertCondition(isOnThread(), mAssertionErrorMessage);
}
@ -139,6 +165,13 @@ import com.facebook.react.common.futures.SimpleSettableFuture;
}, "mqt_" + name);
bgThread.start();
return new MessageQueueThreadImpl(name, simpleSettableFuture.get(5000), exceptionHandler);
try {
return new MessageQueueThreadImpl(
name,
simpleSettableFuture.get(5000, TimeUnit.MILLISECONDS),
exceptionHandler);
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
}

View File

@ -12,29 +12,61 @@ package com.facebook.react.common.futures;
import javax.annotation.Nullable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A super simple Future-like class that can safely notify another Thread when a value is ready.
* Does not support setting errors or canceling.
* Does not support canceling.
*/
public class SimpleSettableFuture<T> {
public class SimpleSettableFuture<T> implements Future<T> {
private final CountDownLatch mReadyLatch = new CountDownLatch(1);
private volatile @Nullable T mResult;
private @Nullable T mResult;
private @Nullable Exception mException;
/**
* Sets the result. If another thread has called {@link #get}, they will immediately receive the
* value. Must only be called once.
* value. set or setException must only be called once.
*/
public void set(T result) {
if (mReadyLatch.getCount() == 0) {
throw new RuntimeException("Result has already been set!");
}
checkNotSet();
mResult = result;
mReadyLatch.countDown();
}
/**
* Sets the eception. If another thread has called {@link #get}, they will immediately receive the
* exception. set or setException must only be called once.
*/
public void setException(Exception exception) {
checkNotSet();
mException = exception;
mReadyLatch.countDown();
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
throw new UnsupportedOperationException();
}
@Override
public boolean isCancelled() {
return false;
}
@Override
public boolean isDone() {
return mReadyLatch.getCount() == 0;
}
@Deprecated
@Override
public T get() throws InterruptedException, ExecutionException {
throw new UnsupportedOperationException("Must use a timeout");
}
/**
* Wait up to the timeout time for another Thread to set a value on this future. If a value has
* already been set, this method will return immediately.
@ -42,21 +74,26 @@ public class SimpleSettableFuture<T> {
* NB: For simplicity, we catch and wrap InterruptedException. Do NOT use this class if you
* are in the 1% of cases where you actually want to handle that.
*/
public @Nullable T get(long timeoutMS) {
@Override
public @Nullable T get(long timeout, TimeUnit unit) throws
InterruptedException, ExecutionException, TimeoutException {
try {
if (!mReadyLatch.await(timeoutMS, TimeUnit.MILLISECONDS)) {
throw new TimeoutException();
if (!mReadyLatch.await(timeout, unit)) {
throw new TimeoutException("Timed out waiting for result");
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (mException != null) {
throw new ExecutionException(mException);
}
return mResult;
}
public static class TimeoutException extends RuntimeException {
public TimeoutException() {
super("Timed out waiting for future");
private void checkNotSet() {
if (mReadyLatch.getCount() == 0) {
throw new RuntimeException("Result has already been set!");
}
}
}