WebWorkers: Allow native modules to be notified when executors are unregistered

Summary: This will allow them to clean up resources when a web worker goes away.

Reviewed By: mhorowitz, lexs

Differential Revision: D2994721

fb-gh-sync-id: c7ca1afc7290e85038cf692a139f6478dba0ef61
shipit-source-id: c7ca1afc7290e85038cf692a139f6478dba0ef61
This commit is contained in:
Andy Street
2016-03-03 04:09:27 -08:00
committed by Facebook Github Bot 3
parent 1bab7c5182
commit 6d5f9ddfff
7 changed files with 157 additions and 58 deletions

View File

@@ -222,8 +222,10 @@ public class CatalystInstanceImpl implements CatalystInstance {
Systrace.unregisterListener(mTraceListener);
synchronouslyDisposeBridgeOnJSThread();
mReactQueueConfiguration.destroy();
}
mReactQueueConfiguration.destroy();
boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0);
if (!wasIdle && !mBridgeIdleListeners.isEmpty()) {
for (NotThreadSafeBridgeIdleDebugListener listener : mBridgeIdleListeners) {
@@ -293,12 +295,12 @@ public class CatalystInstanceImpl implements CatalystInstance {
@Override
public void handleMemoryPressure(final MemoryPressure level) {
mReactQueueConfiguration.getJSQueueThread().runOnQueue(
new Runnable() {
@Override
public void run() {
Assertions.assertNotNull(mBridge).handleMemoryPressure(level);
}
});
new Runnable() {
@Override
public void run() {
Assertions.assertNotNull(mBridge).handleMemoryPressure(level);
}
});
}
/**
@@ -399,12 +401,14 @@ public class CatalystInstanceImpl implements CatalystInstance {
public void call(ExecutorToken executorToken, int moduleId, int methodId, ReadableNativeArray parameters) {
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
// Suppress any callbacks if destroyed - will only lead to sadness.
if (mDestroyed) {
return;
}
synchronized (mTeardownLock) {
// Suppress any callbacks if destroyed - will only lead to sadness.
if (mDestroyed) {
return;
}
mJavaRegistry.call(CatalystInstanceImpl.this, executorToken, moduleId, methodId, parameters);
mJavaRegistry.call(CatalystInstanceImpl.this, executorToken, moduleId, methodId, parameters);
}
}
@Override
@@ -415,17 +419,38 @@ public class CatalystInstanceImpl implements CatalystInstance {
// native modules could be in a bad state so we don't want to call anything on them. We
// still want to trigger the debug listener since it allows instrumentation tests to end and
// check their assertions without waiting for a timeout.
if (!mDestroyed) {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onBatchComplete");
try {
mJavaRegistry.onBatchComplete();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
synchronized (mTeardownLock) {
if (!mDestroyed) {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onBatchComplete");
try {
mJavaRegistry.onBatchComplete();
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}
decrementPendingJSCalls();
}
@Override
public void onExecutorUnregistered(ExecutorToken executorToken) {
mReactQueueConfiguration.getNativeModulesQueueThread().assertIsOnThread();
// Since onCatalystInstanceDestroy happens on the UI thread, we don't want to also execute
// this callback on the native modules thread at the same time. Longer term, onCatalystInstanceDestroy
// should probably be executed on the native modules thread as well instead.
synchronized (mTeardownLock) {
if (!mDestroyed) {
Systrace.beginSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE, "onExecutorUnregistered");
try {
mJavaRegistry.onExecutorUnregistered(executorToken);
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT_JAVA_BRIDGE);
}
}
}
}
}
private class NativeExceptionHandler implements QueueThreadExceptionHandler {

View File

@@ -30,6 +30,7 @@ public class NativeModuleRegistry {
private final List<ModuleDefinition> mModuleTable;
private final Map<Class<? extends NativeModule>, NativeModule> mModuleInstances;
private final ArrayList<OnBatchCompleteListener> mBatchCompleteListenerModules;
private final ArrayList<OnExecutorUnregisteredListener> mOnExecutorUnregisteredListenerModules;
private NativeModuleRegistry(
List<ModuleDefinition> moduleTable,
@@ -37,12 +38,16 @@ public class NativeModuleRegistry {
mModuleTable = moduleTable;
mModuleInstances = moduleInstances;
mBatchCompleteListenerModules = new ArrayList<OnBatchCompleteListener>(mModuleTable.size());
mBatchCompleteListenerModules = new ArrayList<>(mModuleTable.size());
mOnExecutorUnregisteredListenerModules = new ArrayList<>(mModuleTable.size());
for (int i = 0; i < mModuleTable.size(); i++) {
ModuleDefinition definition = mModuleTable.get(i);
if (definition.target instanceof OnBatchCompleteListener) {
mBatchCompleteListenerModules.add((OnBatchCompleteListener) definition.target);
}
if (definition.target instanceof OnExecutorUnregisteredListener) {
mOnExecutorUnregisteredListenerModules.add((OnExecutorUnregisteredListener) definition.target);
}
}
}
@@ -135,6 +140,12 @@ public class NativeModuleRegistry {
}
}
public void onExecutorUnregistered(ExecutorToken executorToken) {
for (int i = 0; i < mOnExecutorUnregisteredListenerModules.size(); i++) {
mOnExecutorUnregisteredListenerModules.get(i).onExecutorDestroyed(executorToken);
}
}
public <T extends NativeModule> T getModule(Class<T> moduleInterface) {
return (T) Assertions.assertNotNull(mModuleInstances.get(moduleInterface));
}

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.react.bridge;
/**
* Interface for a module that will be notified when JS executors have been unregistered from the bridge.
* Note that this will NOT notify listeners about the main executor being destroyed: use
* {@link NativeModule#onCatalystInstanceDestroy()} for that. Once a module has received a
* {@link NativeModule#onCatalystInstanceDestroy()} call, it will not receive any onExecutorUnregistered
* calls.
*/
public interface OnExecutorUnregisteredListener {
void onExecutorDestroyed(ExecutorToken executorToken);
}

View File

@@ -19,4 +19,7 @@ public interface ReactCallback {
@DoNotStrip
void onBatchComplete();
@DoNotStrip
void onExecutorUnregistered(ExecutorToken executorToken);
}