Skip to content
Closed
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 @@ -55,7 +55,6 @@ import com.facebook.systrace.Systrace
import java.util.ArrayDeque
import java.util.LinkedList
import java.util.Queue
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.Volatile
import kotlin.concurrent.read
Expand Down Expand Up @@ -87,20 +86,9 @@ internal constructor(
public var context: ThemedReactContext? = reactContext
private set

private val tagToViewState: ConcurrentHashMap<Int, ViewState>?
private val optimizedTagToViewState: MutableIntObjectMap<ViewState>?
private val tagToViewState: MutableIntObjectMap<ViewState> = MutableIntObjectMap()
private val registryLock = ReentrantReadWriteLock()

init {
if (ReactNativeFeatureFlags.useOptimizedViewRegistryOnAndroid()) {
tagToViewState = null
optimizedTagToViewState = MutableIntObjectMap()
} else {
tagToViewState = ConcurrentHashMap()
optimizedTagToViewState = null
}
}

private val onViewAttachMountItems: Queue<MountItem> = ArrayDeque()

// These are all non-null, until StopSurface is called
Expand Down Expand Up @@ -142,7 +130,9 @@ internal constructor(
return
}

registryPut(surfaceId, ViewState(surfaceId, rootView, rootViewManager, true))
registryLock.write {
tagToViewState[surfaceId] = ViewState(surfaceId, rootView, rootViewManager, true)
}

val runnable: Runnable =
object : GuardedRunnable(checkNotNull(context)) {
Expand Down Expand Up @@ -206,7 +196,7 @@ internal constructor(
if (tagSetForStoppedSurface?.containsKey(tag) == true) {
return true
}
return registryContains(tag)
return registryLock.read { tagToViewState.containsKey(tag) }
}

@UiThread
Expand Down Expand Up @@ -252,42 +242,38 @@ internal constructor(
// Reset all StateWrapper objects
// Since this can happen on any thread, is it possible to race between StateWrapper destruction
// and some accesses from View classes in the UI thread?
registryForEachValue { viewState ->
viewState.stateWrapper?.destroyState()
viewState.stateWrapper = null
registryLock.read {
tagToViewState.forEachValue { viewState ->
viewState.stateWrapper?.destroyState()
viewState.stateWrapper = null

viewState.eventEmitter?.destroy()
viewState.eventEmitter = null
viewState.eventEmitter?.destroy()
viewState.eventEmitter = null
}
}

val runnable = Runnable {
if (ReactNativeFeatureFlags.enableViewRecycling()) {
viewManagerRegistry?.onSurfaceStopped(surfaceId)
}

if (optimizedTagToViewState != null) {
val viewStatesToDelete: ArrayList<ViewState>
registryLock.write {
val tagSetForStoppedSurface =
SparseArrayCompat<Any>().also { this.tagSetForStoppedSurface = it }
viewStatesToDelete = ArrayList(optimizedTagToViewState.size)
optimizedTagToViewState.forEach { key, value ->
tagSetForStoppedSurface[key] = this@SurfaceMountingManager
viewStatesToDelete.add(value)
}
optimizedTagToViewState.clear()
}
for (viewState in viewStatesToDelete) {
onViewStateDeleted(viewState)
// Using this as a placeholder value in the map. We're using SparseArrayCompat
// since it can efficiently represent the list of pending tags
val tagSetForStoppedSurface =
SparseArrayCompat<Any>().also { this.tagSetForStoppedSurface = it }

val viewStatesToDelete: ArrayList<ViewState>
registryLock.write {
viewStatesToDelete = ArrayList(tagToViewState.size)
tagToViewState.forEach { key, value ->
tagSetForStoppedSurface[key] = this@SurfaceMountingManager
viewStatesToDelete.add(value)
}
} else {
val tagSetForStoppedSurface =
SparseArrayCompat<Any>().also { this.tagSetForStoppedSurface = it }
for ((key, value) in tagToViewState!!) {
tagSetForStoppedSurface[key] = this
onViewStateDeleted(value)
}
tagToViewState!!.clear()
tagToViewState.clear()
}
for (viewState in viewStatesToDelete) {
// We must call `onDropViewInstance` on all remaining Views
onViewStateDeleted(viewState)
}

// Evict all views from cache and memory
Expand Down Expand Up @@ -602,7 +588,7 @@ internal constructor(
this.stateWrapper = stateWrapper
this.eventEmitter = eventEmitterWrapper
}
registryPut(reactTag, viewState)
registryLock.write { tagToViewState[reactTag] = viewState }

if (isLayoutable) {
@Suppress("UNCHECKED_CAST")
Expand Down Expand Up @@ -977,17 +963,9 @@ internal constructor(

// TODO T62717437 - Use a flag to determine that these event emitters belong to virtual nodes
// only.
val viewState: ViewState =
if (optimizedTagToViewState != null) {
registryLock.write { optimizedTagToViewState.getOrPut(reactTag) { ViewState(reactTag) } }
} else {
var vs = tagToViewState!![reactTag]
if (vs == null) {
vs = ViewState(reactTag)
tagToViewState!![reactTag] = vs
}
vs
}
val viewState: ViewState = registryLock.write {
tagToViewState.getOrPut(reactTag) { ViewState(reactTag) }
}

val previousEventEmitterWrapper = viewState.eventEmitter
synchronized(viewState) {
Expand Down Expand Up @@ -1096,7 +1074,7 @@ internal constructor(
// To delete we simply remove the tag from the registry.
// We want to rely on the correct set of MountInstructions being sent to the platform,
// or StopSurface being called, so we do not handle deleting descendants of the View.
registryRemove(reactTag)
registryLock.write { tagToViewState.remove(reactTag) }

onViewStateDeleted(viewState)
}
Expand Down Expand Up @@ -1141,51 +1119,13 @@ internal constructor(
}

private fun getViewState(reactTag: Int): ViewState =
registryGet(reactTag)
getNullableViewState(reactTag)
?: throw RetryableMountingLayerException(
"Unable to find viewState for tag $reactTag. Surface stopped: $isStopped"
)

private fun getNullableViewState(reactTag: Int): ViewState? = registryGet(reactTag)

private fun registryGet(tag: Int): ViewState? {
return if (optimizedTagToViewState != null) {
registryLock.read { optimizedTagToViewState[tag] }
} else {
tagToViewState!![tag]
}
}

private fun registryPut(tag: Int, state: ViewState) {
if (optimizedTagToViewState != null) {
registryLock.write { optimizedTagToViewState[tag] = state }
} else {
tagToViewState!![tag] = state
}
}

private fun registryRemove(tag: Int) {
if (optimizedTagToViewState != null) {
registryLock.write { optimizedTagToViewState.remove(tag) }
} else {
tagToViewState!!.remove(tag)
}
}

private fun registryContains(tag: Int): Boolean {
return if (optimizedTagToViewState != null) {
registryLock.read { optimizedTagToViewState.containsKey(tag) }
} else {
tagToViewState!!.containsKey(tag)
}
}

private inline fun registryForEachValue(action: (ViewState) -> Unit) {
if (optimizedTagToViewState != null) {
registryLock.read { optimizedTagToViewState.forEachValue(action) }
} else {
tagToViewState!!.values.forEach(action)
}
private inline fun getNullableViewState(reactTag: Int): ViewState? = registryLock.read {
tagToViewState[reactTag]
}

/** Applies a bitmap as the background of the view with the given tag, if it exists. */
Expand All @@ -1197,16 +1137,18 @@ internal constructor(

public fun printSurfaceState(): Unit {
FLog.e(TAG, "Views created for surface $surfaceId:")
registryForEachValue { viewState ->
val viewManagerName = viewState.viewManager?.name
val view = viewState.view
val parent = if (view != null) view.parent as View? else null
val parentTag = parent?.id
registryLock.read {
tagToViewState.forEachValue { viewState ->
val viewManagerName = viewState.viewManager?.name
val view = viewState.view
val parent = if (view != null) view.parent as View? else null
val parentTag = parent?.id

FLog.e(
TAG,
"<$viewManagerName id=${viewState.reactTag} parentTag=$parentTag isRoot=${viewState.isRoot} />",
)
FLog.e(
TAG,
"<$viewManagerName id=${viewState.reactTag} parentTag=$parentTag isRoot=${viewState.isRoot} />",
)
}
}
}

Expand All @@ -1219,7 +1161,7 @@ internal constructor(
@EventCategoryDef eventCategory: Int,
eventTimestamp: Long,
) {
val viewState = registryGet(reactTag)
val viewState = getNullableViewState(reactTag)
if (viewState == null) {
FLog.i(TAG, "Unable to invoke event: %s for reactTag: %d", eventName, reactTag)
return
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<<7e91da9df64ce42424101d7d72356c4a>>
* @generated SignedSource<<1167623751686dc836ff8209fe55cbb1>>
*/

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

/**
* Use MutableIntObjectMap with ReadWriteLock instead of ConcurrentHashMap for the view registry in SurfaceMountingManager to reduce memory overhead and GC pressure.
*/
@JvmStatic
public fun useOptimizedViewRegistryOnAndroid(): Boolean = accessor.useOptimizedViewRegistryOnAndroid()

/**
* Use shared animation backend in C++ Animated
*/
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<<3c0e10dee93b76f3e66ca79d26f2b4f2>>
* @generated SignedSource<<4203cfc47d90d9d11b3ae97d932e8200>>
*/

/**
Expand Down Expand Up @@ -100,7 +100,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
private var useFabricInteropCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useNestedScrollViewAndroidCache: Boolean? = null
private var useOptimizedViewRegistryOnAndroidCache: Boolean? = null
private var useSharedAnimatedBackendCache: Boolean? = null
private var useTraitHiddenOnAndroidCache: Boolean? = null
private var useTurboModuleInteropCache: Boolean? = null
Expand Down Expand Up @@ -829,15 +828,6 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
return cached
}

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

override fun useSharedAnimatedBackend(): Boolean {
var cached = useSharedAnimatedBackendCache
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<<e9cc7d01ac1884710f131fbf375f3502>>
* @generated SignedSource<<7f472ecf6b1cacee2087a6df862c5274>>
*/

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

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

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

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

@DoNotStrip @JvmStatic public external fun useTraitHiddenOnAndroid(): 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<<061d668cf04041f4d3d2f48f11dc739f>>
* @generated SignedSource<<f0c68f41f2be4c283965970413bb7d31>>
*/

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

override fun useNestedScrollViewAndroid(): Boolean = false

override fun useOptimizedViewRegistryOnAndroid(): Boolean = false

override fun useSharedAnimatedBackend(): Boolean = false

override fun useTraitHiddenOnAndroid(): 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<<fa85d53197cbdce6b1373fbadf3cf2b9>>
* @generated SignedSource<<f40636a4203d5741bb9a034f1a1a482b>>
*/

/**
Expand Down Expand Up @@ -104,7 +104,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
private var useFabricInteropCache: Boolean? = null
private var useNativeViewConfigsInBridgelessModeCache: Boolean? = null
private var useNestedScrollViewAndroidCache: Boolean? = null
private var useOptimizedViewRegistryOnAndroidCache: Boolean? = null
private var useSharedAnimatedBackendCache: Boolean? = null
private var useTraitHiddenOnAndroidCache: Boolean? = null
private var useTurboModuleInteropCache: Boolean? = null
Expand Down Expand Up @@ -913,16 +912,6 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
return cached
}

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

override fun useSharedAnimatedBackend(): Boolean {
var cached = useSharedAnimatedBackendCache
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<<a460b28871fc3f928dcc2d1a3bf66422>>
* @generated SignedSource<<527607482377488c5d5126dcb78a16a1>>
*/

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

@DoNotStrip public fun useNestedScrollViewAndroid(): Boolean

@DoNotStrip public fun useOptimizedViewRegistryOnAndroid(): Boolean

@DoNotStrip public fun useSharedAnimatedBackend(): Boolean

@DoNotStrip public fun useTraitHiddenOnAndroid(): Boolean
Expand Down
Loading
Loading