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 коммит произвёл facebook-github-bot-7
Родитель f2d84456e5
Коммит 8de172e92d
4 изменённых файлов: 122 добавлений и 56 удалений

Просмотреть файл

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

Просмотреть файл

@ -9,6 +9,9 @@
package com.facebook.react.bridge.queue; package com.facebook.react.bridge.queue;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import com.facebook.proguard.annotations.DoNotStrip; import com.facebook.proguard.annotations.DoNotStrip;
/** /**
@ -23,6 +26,13 @@ public interface MessageQueueThread {
@DoNotStrip @DoNotStrip
void runOnQueue(Runnable runnable); 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. * @return whether the current Thread is also the Thread associated with this MessageQueueThread.
*/ */

Просмотреть файл

@ -9,6 +9,10 @@
package com.facebook.react.bridge.queue; 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 android.os.Looper;
import com.facebook.common.logging.FLog; 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. * if it is being submitted from the same queue Thread.
*/ */
@DoNotStrip @DoNotStrip
@Override
public void runOnQueue(Runnable runnable) { public void runOnQueue(Runnable runnable) {
if (mIsFinished) { if (mIsFinished) {
FLog.w( FLog.w(
@ -55,9 +60,29 @@ import com.facebook.react.common.futures.SimpleSettableFuture;
mHandler.post(runnable); 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. * @return whether the current Thread is also the Thread associated with this MessageQueueThread.
*/ */
@Override
public boolean isOnThread() { public boolean isOnThread() {
return mLooper.getThread() == Thread.currentThread(); 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 * Asserts {@link #isOnThread()}, throwing a {@link AssertionException} (NOT an
* {@link AssertionError}) if the assertion fails. * {@link AssertionError}) if the assertion fails.
*/ */
@Override
public void assertIsOnThread() { public void assertIsOnThread() {
SoftAssertions.assertCondition(isOnThread(), mAssertionErrorMessage); SoftAssertions.assertCondition(isOnThread(), mAssertionErrorMessage);
} }
@ -139,6 +165,13 @@ import com.facebook.react.common.futures.SimpleSettableFuture;
}, "mqt_" + name); }, "mqt_" + name);
bgThread.start(); 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);
}
} }
} }

Просмотреть файл

@ -12,29 +12,61 @@ package com.facebook.react.common.futures;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; 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. * 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 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 * 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) { public void set(T result) {
if (mReadyLatch.getCount() == 0) { checkNotSet();
throw new RuntimeException("Result has already been set!");
}
mResult = result; mResult = result;
mReadyLatch.countDown(); 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 * 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. * 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 * 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. * 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 { try {
if (!mReadyLatch.await(timeoutMS, TimeUnit.MILLISECONDS)) { if (!mReadyLatch.await(timeout, unit)) {
throw new TimeoutException(); throw new TimeoutException("Timed out waiting for result");
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
if (mException != null) {
throw new ExecutionException(mException);
}
return mResult; return mResult;
} }
public static class TimeoutException extends RuntimeException { private void checkNotSet() {
if (mReadyLatch.getCount() == 0) {
public TimeoutException() { throw new RuntimeException("Result has already been set!");
super("Timed out waiting for future");
} }
} }
} }