Repository navigation
Commit 3b4abb3
Make the view manager prop setter caches thread-safe (#59245)
Summary:
`ViewManagersPropertyCache.CLASS_PROPS_CACHE` and the two setter maps in `ViewManagerPropertyUpdater` are static `HashMap`s. Two threads write them:
- the UI thread, when it updates the props of a view (`ViewManagerPropertyUpdater.updateProps` → `findManagerSetter`);
- the JS thread, when it builds a view config (`getConstantsForViewManager` → `ViewManager.nativeProps`).
In bridgeless mode, JS gets the view config of a lazy view manager on first use, during render. At that time, the UI thread can mount other views. Concurrent writes to a `HashMap` can lose entries or break the map.
This change uses `ConcurrentHashMap` for the three maps. The get-then-put code does not change. If two threads compute the same entry, the values are equal, so the second write is harmless. I did not use `computeIfAbsent`, because `getNativePropSettersForViewManagerClass` calls itself for the superclass.
This came up in the review of #59173, which sends the eager view managers through the same on-demand path.
## Changelog:
[ANDROID] [FIXED] - Make the view manager prop setter caches thread-safe
Pull Request resolved: #59245
Test Plan:
Unit tests: `./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest --tests "com.facebook.react.uimanager.*"` passes (230 tests, 0 failures).
Device: Xiaomi Mi Note 2 (Snapdragon 821, Android 8.0). App: a production Expo app, release build, React Native 0.88.0, Hermes. I compiled this change and #59173 against 0.88.0 and replaced the same classes in `react-android-0.88.0-release.aar`. I measured from process start to the first frame with rows in the first list, 12 cold starts for each build.
| Build | Median | Range | `getConstants()` |
|---|---|---|---|
| Stock 0.88.0 | 2416 ms | 2335–2477 ms | 215 ms (45 keys) |
| #59173 + this change | 2243 ms | 2187–2406 ms | 45 ms (5 keys) |
To see the cost of this change alone, I also used a build whose JS does not call `getConstants()` at startup, so the first view configs come from `getConstantsForViewManager()` during render. Without this change: 2189 ms median (2140–2236 ms). With it: 2192 ms median (2156–2244 ms). I see no difference.
Same chat app with threads list and messages. With this change, I opened the chat list, a thread, settings, the theme list, the profile and the contacts, and scrolled. Logcat shows no crash and no view config errors.
Reviewed By: christophpurrer
Differential Revision: D126631569
Pulled By: javache
fbshipit-source-id: ae455c2a152f9dcf6e5bec172005db9e654e71931 parent 8f5a1c0 commit 3b4abb3
2 files changed
Lines changed: 9 additions & 3 deletions
File tree
- packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
| 15 | + | |
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
| |||
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
34 | | - | |
35 | | - | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
36 | 40 | | |
37 | 41 | | |
38 | 42 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
| 24 | + | |
24 | 25 | | |
25 | 26 | | |
26 | 27 | | |
| |||
29 | 30 | | |
30 | 31 | | |
31 | 32 | | |
32 | | - | |
| 33 | + | |
| 34 | + | |
33 | 35 | | |
34 | 36 | | |
35 | 37 | | |
| |||
0 commit comments