Skip to content

Commit 006f5af

Browse files
JoshuaGrossfacebook-github-bot
authored andcommitted
Guard against unsafe EventEmitter setup and teardown
Summary: Because of T92179998, T93607943, and T93394807, we are still seeking resolution to tricky crashes wrt the use of EventEmitters. I believe the recent spike is because of two recent changes: we pass in EventEmitters earlier, during PreAllocation; and we clean them up earlier, during stopSurface, to avoid jsi::~Pointer crashes. Additionally, the gating previously added around the PreAllocation path was incorrect and led to more nullptrs being passed around as EventEmitters. To mitigate these issues: 1) I am adding/fixing gating to preallocation and early cleanup paths 2) I am making EventEmitterWrapper more resilient by ensuring EventEmitter is non-null before invoking it. 3) I am making sure that in more cases, we pass a non-null EventEmitter pointer to Java. 4) I am backing out the synchronization in EventEmitterWrapper (java side) as that did not resolve the issue and is a pessimisation There are older, unchanged paths that could still be passing in nullptr as the EventEmitter (Update and Create). As those have not changed recently, I'm not going to fix those cases and instead, we can now rely on the caller to ensure that the EventEmitter is non-null before calling. Changelog: [internal] Differential Revision: D29252806 fbshipit-source-id: 5c68d95fa2465afe45e0083a0685c8c1abf31619
1 parent 52a9fed commit 006f5af

File tree

5 files changed

+38
-26
lines changed

5 files changed

+38
-26
lines changed

ReactAndroid/src/main/java/com/facebook/react/config/ReactFeatureFlags.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ public static boolean isMapBufferSerializationEnabled() {
6666

6767
public static boolean enableLockFreeEventDispatcher = false;
6868

69+
public static boolean enableAggressiveEventEmitterCleanup = false;
70+
6971
//
7072
// ScrollView C++ UpdateState vs onScroll race fixes
7173
//

ReactAndroid/src/main/java/com/facebook/react/fabric/events/EventEmitterWrapper.java

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,11 @@ private native void invokeUniqueEvent(
4848
* @param params {@link WritableMap} payload of the event
4949
*/
5050
public void invoke(@NonNull String eventName, @Nullable WritableMap params) {
51-
synchronized (mHybridData) {
52-
if (!isValid()) {
53-
return;
54-
}
55-
NativeMap payload = params == null ? new WritableNativeMap() : (NativeMap) params;
56-
invokeEvent(eventName, payload);
51+
if (!isValid()) {
52+
return;
5753
}
54+
NativeMap payload = params == null ? new WritableNativeMap() : (NativeMap) params;
55+
invokeEvent(eventName, payload);
5856
}
5957

6058
/**
@@ -66,20 +64,16 @@ public void invoke(@NonNull String eventName, @Nullable WritableMap params) {
6664
*/
6765
public void invokeUnique(
6866
@NonNull String eventName, @Nullable WritableMap params, int customCoalesceKey) {
69-
synchronized (mHybridData) {
70-
if (!isValid()) {
71-
return;
72-
}
73-
NativeMap payload = params == null ? new WritableNativeMap() : (NativeMap) params;
74-
invokeUniqueEvent(eventName, payload, customCoalesceKey);
67+
if (!isValid()) {
68+
return;
7569
}
70+
NativeMap payload = params == null ? new WritableNativeMap() : (NativeMap) params;
71+
invokeUniqueEvent(eventName, payload, customCoalesceKey);
7672
}
7773

7874
public void destroy() {
79-
synchronized (mHybridData) {
80-
if (mHybridData != null) {
81-
mHybridData.resetNative();
82-
}
75+
if (mHybridData != null) {
76+
mHybridData.resetNative();
8377
}
8478
}
8579

ReactAndroid/src/main/java/com/facebook/react/fabric/jni/Binding.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,11 +1151,14 @@ void Binding::preallocateShadowView(
11511151
}
11521152

11531153
// Do not hold a reference to javaEventEmitter from the C++ side.
1154-
auto javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs();
1154+
local_ref<EventEmitterWrapper::JavaPart> javaEventEmitter = nullptr;
11551155
if (enableEarlyEventEmitterUpdate_) {
11561156
SharedEventEmitter eventEmitter = shadowView.eventEmitter;
1157-
EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter);
1158-
cEventEmitter->eventEmitter = eventEmitter;
1157+
if (eventEmitter != nullptr) {
1158+
javaEventEmitter = EventEmitterWrapper::newObjectJavaArgs();
1159+
EventEmitterWrapper *cEventEmitter = cthis(javaEventEmitter);
1160+
cEventEmitter->eventEmitter = eventEmitter;
1161+
}
11591162
}
11601163

11611164
local_ref<ReadableMap::javaobject> props = castReadableMap(
@@ -1169,7 +1172,7 @@ void Binding::preallocateShadowView(
11691172
component.get(),
11701173
props.get(),
11711174
(javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr),
1172-
javaEventEmitter.get(),
1175+
(javaEventEmitter != nullptr ? javaEventEmitter.get() : nullptr),
11731176
isLayoutableShadowNode);
11741177
}
11751178

ReactAndroid/src/main/java/com/facebook/react/fabric/jni/EventEmitterWrapper.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,26 @@ EventEmitterWrapper::initHybrid(jni::alias_ref<jclass>) {
2121
void EventEmitterWrapper::invokeEvent(
2222
std::string eventName,
2323
NativeMap *payload) {
24-
eventEmitter->dispatchEvent(
25-
eventName, payload->consume(), EventPriority::AsynchronousBatched);
24+
// It is marginal, but possible for this to be constructed without a valid
25+
// EventEmitter. In those cases, make sure we noop/blackhole events instead of
26+
// crashing.
27+
if (eventEmitter != nullptr) {
28+
eventEmitter->dispatchEvent(
29+
eventName, payload->consume(), EventPriority::AsynchronousBatched);
30+
}
2631
}
2732

2833
void EventEmitterWrapper::invokeUniqueEvent(
2934
std::string eventName,
3035
NativeMap *payload,
3136
int customCoalesceKey) {
3237
// TODO: customCoalesceKey currently unused
33-
eventEmitter->dispatchUniqueEvent(eventName, payload->consume());
38+
// It is marginal, but possible for this to be constructed without a valid
39+
// EventEmitter. In those cases, make sure we noop/blackhole events instead of
40+
// crashing.
41+
if (eventEmitter != nullptr) {
42+
eventEmitter->dispatchUniqueEvent(eventName, payload->consume());
43+
}
3444
}
3545

3646
void EventEmitterWrapper::registerNatives() {

ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.facebook.react.bridge.SoftAssertions;
2828
import com.facebook.react.bridge.UiThreadUtil;
2929
import com.facebook.react.common.build.ReactBuildConfig;
30+
import com.facebook.react.config.ReactFeatureFlags;
3031
import com.facebook.react.fabric.events.EventEmitterWrapper;
3132
import com.facebook.react.fabric.mounting.MountingManager.MountItemExecutor;
3233
import com.facebook.react.fabric.mounting.mountitems.MountItem;
@@ -250,9 +251,11 @@ public void stopSurface() {
250251
viewState.mStateWrapper.destroyState();
251252
viewState.mStateWrapper = null;
252253
}
253-
if (viewState.mEventEmitter != null) {
254-
viewState.mEventEmitter.destroy();
255-
viewState.mEventEmitter = null;
254+
if (ReactFeatureFlags.enableAggressiveEventEmitterCleanup) {
255+
if (viewState.mEventEmitter != null) {
256+
viewState.mEventEmitter.destroy();
257+
viewState.mEventEmitter = null;
258+
}
256259
}
257260
}
258261

0 commit comments

Comments
 (0)