Skip to content

Commit 21fae86

Browse files
edwii-zhumeta-codesync[bot]
authored andcommitted
Fix DynamicFromArray thread-safety by using ThreadLocal pool (#55793)
Summary: `DynamicFromArray` uses a plain `Pools.SimplePool` shared across all threads. When two threads concurrently access the same pooled `DynamicFromArray` instance — one calling `recycle()` while the other reads from it — the reader sees `array == null` and throws `IllegalStateException("This dynamic value has been recycled")`. The sibling class `DynamicFromMap` was fixed for this exact issue in #17842 (2018) by switching to `ThreadLocal<SimplePool>`, giving each thread its own isolated pool. A reviewer on that PR [noted](#17842 (comment)) the same fix should be applied to `DynamicFromArray`, but it never was. This PR applies the identical `ThreadLocal` pattern from `DynamicFromMap` to `DynamicFromArray`. ## Changelog: [ANDROID] [FIXED] - Fix thread-safety crash in `DynamicFromArray` by using `ThreadLocal<SimplePool>` instead of a shared `SimplePool`, matching the existing fix in `DynamicFromMap` Pull Request resolved: #55793 Test Plan: The change mirrors the existing pattern in `DynamicFromMap.kt` in the same directory — the only difference is the type parameter. The crash is a race condition that is not reliably reproducible in a unit test, but can be confirmed by: 1. Verify `DynamicFromArray.kt` now uses `ThreadLocal<Pools.SimplePool<DynamicFromArray>>` for its pool, `pool.get()?.acquire()` in `create()`, and `pool.get()?.release(this)` in `recycle()` 2. Verify this matches the pattern in `DynamicFromMap.kt` line-for-line 3. Build and run the Android app — no behavioral change expected since the pooling logic is identical, just thread-isolated Reviewed By: cortinico Differential Revision: D94676330 Pulled By: javache fbshipit-source-id: 6ba0be783dc44c913bf57040d57fd98e184636ba
1 parent c3acbc4 commit 21fae86

1 file changed

Lines changed: 9 additions & 4 deletions

File tree

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/bridge/DynamicFromArray.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
package com.facebook.react.bridge
99

10-
import androidx.core.util.Pools
10+
import androidx.core.util.Pools.SimplePool
1111

1212
/** Implementation of Dynamic wrapping a ReadableArray. */
1313
internal class DynamicFromArray private constructor() : Dynamic {
@@ -17,7 +17,7 @@ internal class DynamicFromArray private constructor() : Dynamic {
1717
override fun recycle() {
1818
array = null
1919
index = -1
20-
pool.release(this)
20+
pool.get()?.release(this)
2121
}
2222

2323
override val type: ReadableType
@@ -48,11 +48,16 @@ internal class DynamicFromArray private constructor() : Dynamic {
4848
array?.getString(index) ?: throw IllegalStateException("This dynamic value has been recycled")
4949

5050
companion object {
51-
private val pool = Pools.SimplePool<DynamicFromArray>(10)
51+
private val pool: ThreadLocal<SimplePool<DynamicFromArray>> =
52+
object : ThreadLocal<SimplePool<DynamicFromArray>>() {
53+
override fun initialValue(): SimplePool<DynamicFromArray> {
54+
return SimplePool(10)
55+
}
56+
}
5257

5358
@JvmStatic
5459
fun create(array: ReadableArray, index: Int): DynamicFromArray {
55-
val dynamic = pool.acquire() ?: DynamicFromArray()
60+
val dynamic = pool.get()?.acquire() ?: DynamicFromArray()
5661
dynamic.array = array
5762
dynamic.index = index
5863
return dynamic

0 commit comments

Comments
 (0)