Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@ import com.facebook.proguard.annotations.DoNotStripAny
import com.facebook.react.bridge.NativeMap
import com.facebook.react.bridge.ReadableNativeMap
import com.facebook.react.bridge.WritableMap
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.common.mapbuffer.ReadableMapBuffer
import com.facebook.react.uimanager.ReferenceStateWrapper
import com.facebook.react.uimanager.StateWrapper

/**
* This class holds reference to the C++ EventEmitter object. Instances of this class are created on
* the Bindings.cpp, where the pointer to the C++ event emitter is set.
*/
@SuppressLint("MissingNativeLoadLibrary")
@DoNotStripAny
@OptIn(UnstableReactNativeAPI::class)
internal class StateWrapperImpl private constructor() : HybridClassBase(), ReferenceStateWrapper {

private external fun initHybrid()
Expand All @@ -33,7 +36,7 @@ internal class StateWrapperImpl private constructor() : HybridClassBase(), Refer

private external fun getStateDataReferenceImpl(): Any?

external fun updateStateImpl(map: NativeMap)
external fun updateStateImpl(map: NativeMap, updateMode: Int)

override val stateDataMapBuffer: ReadableMapBuffer?
get() {
Expand Down Expand Up @@ -67,11 +70,15 @@ internal class StateWrapperImpl private constructor() : HybridClassBase(), Refer
}

override fun updateState(map: WritableMap) {
updateState(map, StateWrapper.UpdateMode.Asynchronous)
}

override fun updateState(map: WritableMap, updateMode: StateWrapper.UpdateMode) {
if (!isValid) {
FLog.e(TAG, "Race between StateWrapperImpl destruction and updateState")
return
}
updateStateImpl(map as NativeMap)
updateStateImpl(map as NativeMap, updateMode.value)
}

override fun destroyState() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ internal class MountItemDispatcher(
}

private var inDispatch: Boolean = false
private var followUpDispatchRequired: Boolean = false
var batchedExecutionTime: Long = 0L
private set

Expand Down Expand Up @@ -93,24 +94,29 @@ internal class MountItemDispatcher(
@UiThread
@ThreadConfined(UI)
fun tryDispatchMountItems() {
// If we're already dispatching, don't reenter.
// Reentrance can potentially happen a lot on Android in Fabric because `updateState` from the
// mounting layer causes mount items to be dispatched synchronously. We want to 1) make sure we
// don't reenter in those cases, but 2) still execute those queued instructions synchronously.
// This is a pretty blunt tool, but we might not have better options since we really don't want
// to execute anything out-of-order.
// If we're already dispatching, don't reenter but signal that a follow-up dispatch is
// needed. This follows the same pattern as iOS's RCTMountingManager::initiateTransaction,
// which uses _followUpTransactionRequired flag to ensure mount items
// enqueued during dispatch (e.g., from synchronous state updates triggered by view layout)
// are processed in the same frame rather than deferred to the next one.
if (inDispatch) {
if (ReactNativeFeatureFlags.enableFollowUpMountItemDispatchAndroid()) {
followUpDispatchRequired = true
}
return
}

inDispatch = true
do {
followUpDispatchRequired = false
inDispatch = true

try {
dispatchMountItems()
} finally {
// Clean up after running dispatchMountItems - even if an exception was thrown
inDispatch = false
}
try {
dispatchMountItems()
} finally {
// Clean up after running dispatchMountItems - even if an exception was thrown
inDispatch = false
}
} while (followUpDispatchRequired)

// We call didDispatchMountItems regardless of whether we actually dispatched anything, since
// NativeAnimatedModule relies on this for executing any animations that may have been
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<a39494512e761e266940e5d1853f38fc>>
* @generated SignedSource<<4171750cfc627709dcd32ccb67131dda>>
*/

/**
Expand Down Expand Up @@ -192,6 +192,12 @@ public object ReactNativeFeatureFlags {
@JvmStatic
public fun enableFlexboxAutoMinSizeInStrictMode(): Boolean = accessor.enableFlexboxAutoMinSizeInStrictMode()

/**
* When mount items are queued while MountItemDispatcher is already dispatching on Android (e.g. from a synchronous state update), dispatch them in the same frame instead of deferring them to the next one.
*/
@JvmStatic
public fun enableFollowUpMountItemDispatchAndroid(): Boolean = accessor.enableFollowUpMountItemDispatchAndroid()

/**
* Enables font scale changes updating layout for measurable nodes.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<cd1753241fa2f51c23cd4e01ffe3f18c>>
* @generated SignedSource<<7928e03a82c0fd05eb5a1623959402c4>>
*/

/**
Expand Down Expand Up @@ -47,6 +47,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var enableFabricCommitBranchingMergeOnMainThreadCache: Boolean? = null
private var enableFabricLogsCache: Boolean? = null
private var enableFlexboxAutoMinSizeInStrictModeCache: Boolean? = null
private var enableFollowUpMountItemDispatchAndroidCache: Boolean? = null
private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null
private var enableIOSCompressedTextFrameAdjustmentCache: Boolean? = null
private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null
Expand Down Expand Up @@ -353,6 +354,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}

override fun enableFollowUpMountItemDispatchAndroid(): Boolean {
var cached = enableFollowUpMountItemDispatchAndroidCache
if (cached == null) {
cached = ReactNativeFeatureFlagsCxxInterop.enableFollowUpMountItemDispatchAndroid()
enableFollowUpMountItemDispatchAndroidCache = cached
}
return cached
}

override fun enableFontScaleChangesUpdatingLayout(): Boolean {
var cached = enableFontScaleChangesUpdatingLayoutCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<eb8737673aaec0b1eda6ae2ebc0b9970>>
* @generated SignedSource<<893c2c5612cff8fe044bead40c2fc603>>
*/

/**
Expand Down Expand Up @@ -82,6 +82,8 @@ public object ReactNativeFeatureFlagsCxxInterop {

@DoNotStrip @JvmStatic public external fun enableFlexboxAutoMinSizeInStrictMode(): Boolean

@DoNotStrip @JvmStatic public external fun enableFollowUpMountItemDispatchAndroid(): Boolean

@DoNotStrip @JvmStatic public external fun enableFontScaleChangesUpdatingLayout(): Boolean

@DoNotStrip @JvmStatic public external fun enableIOSCompressedTextFrameAdjustment(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<890e204b6cf8afef481f64de44766edf>>
* @generated SignedSource<<0093c0cd89ab032619b8a77146af32ab>>
*/

/**
Expand Down Expand Up @@ -77,6 +77,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi

override fun enableFlexboxAutoMinSizeInStrictMode(): Boolean = false

override fun enableFollowUpMountItemDispatchAndroid(): Boolean = false

override fun enableFontScaleChangesUpdatingLayout(): Boolean = true

override fun enableIOSCompressedTextFrameAdjustment(): Boolean = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<9044f0df865ba9d7668f9ff391d21911>>
* @generated SignedSource<<330c3dd9643d65d443d3fdc6ac71055e>>
*/

/**
Expand Down Expand Up @@ -51,6 +51,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var enableFabricCommitBranchingMergeOnMainThreadCache: Boolean? = null
private var enableFabricLogsCache: Boolean? = null
private var enableFlexboxAutoMinSizeInStrictModeCache: Boolean? = null
private var enableFollowUpMountItemDispatchAndroidCache: Boolean? = null
private var enableFontScaleChangesUpdatingLayoutCache: Boolean? = null
private var enableIOSCompressedTextFrameAdjustmentCache: Boolean? = null
private var enableIOSTextBaselineOffsetPerLineCache: Boolean? = null
Expand Down Expand Up @@ -384,6 +385,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}

override fun enableFollowUpMountItemDispatchAndroid(): Boolean {
var cached = enableFollowUpMountItemDispatchAndroidCache
if (cached == null) {
cached = currentProvider.enableFollowUpMountItemDispatchAndroid()
accessedFeatureFlags.add("enableFollowUpMountItemDispatchAndroid")
enableFollowUpMountItemDispatchAndroidCache = cached
}
return cached
}

override fun enableFontScaleChangesUpdatingLayout(): Boolean {
var cached = enableFontScaleChangesUpdatingLayoutCache
if (cached == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<cfd99e53a077549124370e6bb123ba43>>
* @generated SignedSource<<abedea0a0379547a304f85f8bc5b259b>>
*/

/**
Expand Down Expand Up @@ -77,6 +77,8 @@ public interface ReactNativeFeatureFlagsProvider {

@DoNotStrip public fun enableFlexboxAutoMinSizeInStrictMode(): Boolean

@DoNotStrip public fun enableFollowUpMountItemDispatchAndroid(): Boolean

@DoNotStrip public fun enableFontScaleChangesUpdatingLayout(): Boolean

@DoNotStrip public fun enableIOSCompressedTextFrameAdjustment(): Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package com.facebook.react.uimanager

import com.facebook.react.bridge.ReadableNativeMap
import com.facebook.react.bridge.WritableMap
import com.facebook.react.common.annotations.UnstableReactNativeAPI
import com.facebook.react.common.mapbuffer.ReadableMapBuffer

/**
Expand All @@ -17,6 +18,15 @@ import com.facebook.react.common.mapbuffer.ReadableMapBuffer
* by calling updateState, which communicates state back to the C++ layer.
*/
public interface StateWrapper {
/**
* Maps to EventQueue::UpdateMode in C++. Controls how state updates are flushed (Async or Sync).
*/
@UnstableReactNativeAPI
public enum class UpdateMode(public val value: Int) {
Asynchronous(0),
unstable_Immediate(1),
}

/**
* Get a ReadableMapBuffer object from the C++ layer, which is a K/V map of short keys to values.
*
Expand All @@ -35,6 +45,12 @@ public interface StateWrapper {
*/
public fun updateState(map: WritableMap)

/**
* Pass a map of values back to the C++ layer. updateMode controls whether the update is queued
* asynchronously or flushed immediately.
*/
@UnstableReactNativeAPI public fun updateState(map: WritableMap, updateMode: UpdateMode)

/**
* Mark state as unused and clean up in Java and in native. This should be called as early as
* possible when you know a StateWrapper will no longer be used. If there's ANY chance of it being
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <fbjni/fbjni.h>
#include <react/featureflags/ReactNativeFeatureFlags.h>
#include <react/jni/ReadableNativeMap.h>
#include <react/renderer/core/EventQueue.h>
#include <react/renderer/mapbuffer/MapBuffer.h>
#include <react/renderer/mapbuffer/MapBufferBuilder.h>

Expand Down Expand Up @@ -50,12 +51,13 @@ jni::local_ref<jobject> StateWrapperImpl::getStateDataReferenceImpl() {
return nullptr;
}

void StateWrapperImpl::updateStateImpl(NativeMap* map) {
void StateWrapperImpl::updateStateImpl(NativeMap* map, jint updateMode) {
if (state_) {
// Get folly::dynamic from map
auto dynamicMap = map->consume();
// Set state
state_->updateState(std::move(dynamicMap));
state_->updateState(
std::move(dynamicMap), static_cast<EventQueue::UpdateMode>(updateMode));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class StateWrapperImpl : public jni::HybridClass<StateWrapperImpl, StateWrapper>
jni::local_ref<JReadableMapBuffer::jhybridobject> getStateMapBufferDataImpl();
jni::local_ref<ReadableNativeMap::jhybridobject> getStateDataImpl();
jni::local_ref<jobject> getStateDataReferenceImpl();
void updateStateImpl(NativeMap *map);
void updateStateImpl(NativeMap *map, jint updateMode);
void setState(std::shared_ptr<const State> state);
std::shared_ptr<const State> getState() const;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<e6f56d60147d5c08e0b38549b9956e52>>
* @generated SignedSource<<251b3d06d3969eb89068403e3c07346e>>
*/

/**
Expand Down Expand Up @@ -201,6 +201,12 @@ class ReactNativeFeatureFlagsJavaProvider
return method(javaProvider_);
}

bool enableFollowUpMountItemDispatchAndroid() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableFollowUpMountItemDispatchAndroid");
return method(javaProvider_);
}

bool enableFontScaleChangesUpdatingLayout() override {
static const auto method =
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("enableFontScaleChangesUpdatingLayout");
Expand Down Expand Up @@ -712,6 +718,11 @@ bool JReactNativeFeatureFlagsCxxInterop::enableFlexboxAutoMinSizeInStrictMode(
return ReactNativeFeatureFlags::enableFlexboxAutoMinSizeInStrictMode();
}

bool JReactNativeFeatureFlagsCxxInterop::enableFollowUpMountItemDispatchAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableFollowUpMountItemDispatchAndroid();
}

bool JReactNativeFeatureFlagsCxxInterop::enableFontScaleChangesUpdatingLayout(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
return ReactNativeFeatureFlags::enableFontScaleChangesUpdatingLayout();
Expand Down Expand Up @@ -1134,6 +1145,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
makeNativeMethod(
"enableFlexboxAutoMinSizeInStrictMode",
JReactNativeFeatureFlagsCxxInterop::enableFlexboxAutoMinSizeInStrictMode),
makeNativeMethod(
"enableFollowUpMountItemDispatchAndroid",
JReactNativeFeatureFlagsCxxInterop::enableFollowUpMountItemDispatchAndroid),
makeNativeMethod(
"enableFontScaleChangesUpdatingLayout",
JReactNativeFeatureFlagsCxxInterop::enableFontScaleChangesUpdatingLayout),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @generated SignedSource<<25d66101785e737822e86d218789912d>>
* @generated SignedSource<<913ddf4c8e4d0b4f897ff51e6d403b69>>
*/

/**
Expand Down Expand Up @@ -111,6 +111,9 @@ class JReactNativeFeatureFlagsCxxInterop
static bool enableFlexboxAutoMinSizeInStrictMode(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

static bool enableFollowUpMountItemDispatchAndroid(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

static bool enableFontScaleChangesUpdatingLayout(
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);

Expand Down
Loading
Loading