Conversation
#45072) # Why Concurrent `Record.toDictionary` calls on Records sharing `@Field` class instances (e.g. expo-video's VideoTrack payloads emitted from multiple background Tasks) causes crash #40688 (comment). Also @AdityaPahilwani reached out to me with below stack trace that happens because of the same issue (concurrent video events dispatched from different queues that uses same field/record) ``` Thread 54 Crashed: 0 Quince 0x00000001044a0c30 Field.key.getter + 56 1 Quince 0x00000001044a0c30 Field.key.getter + 56 2 Quince 0x00000001044a0c30 Field.key.getter + 56 (Field.swift:19) 3 Quince 0x00000001044a1508 protocol witness for AnyFieldInternal.key.getter in conformance Field<A> + 20 4 Quince 0x00000001044bae98 closure #1 (String?, Any) in fieldsOf(Record) + 0 (Record.swift:77) 5 Quince 0x00000001044bae98 specialized fieldsOf(Record) + 456 6 Quince 0x00000001044bae98 specialized fieldsOf(Record) + 456 7 Quince 0x00000001044bae98 specialized fieldsOf(Record) + 456 (Record.swift:76) 8 Quince 0x00000001044ba578 Record.toDictionary(appContext: AppContext?) + 32 9 Quince 0x00000001044ba578 Record.toDictionary(appContext: AppContext?) + 32 (Record.swift:63) 10 Quince 0x000000010448d088 DynamicConvertibleType.convertResult<A>(_: A, appContext: AppContext) + 204 (DynamicConvertibleType.swift:26) 11 Quince 0x000000010448d19c protocol witness for AnyDynamicType.convertResult<A>(_: A1, appContext: AppContext) in conformance DynamicConvertibleType + 16 12 Quince 0x000000010449053c DynamicOptionalType.convertResult<A>(_: A, appContext: AppContext) + 100 (DynamicOptionalType.swift:40) 13 Quince 0x000000010449064c protocol witness for AnyDynamicType.convertResult<A>(_: A1, appContext: AppContext) in conformance DynamicOptionalType + 12 14 Quince 0x00000001044c3660 $s15ExpoModulesCore11ConversionsV21convertFunctionResult_10appContext11dynamicTypeypxSg_AA03AppI0CSgAA010AnyDynamicK0_pSgtlFZyp_Tt3g5 + 628 (Conversions.swift:193) 15 Quince 0x00000001044bac30 closure #1 (inout [String : Any], AnyFieldInternal) in Record.toDictionary(appContext: AppContext?) + 164 16 Quince 0x00000001044bb43c partial apply for closure #1 (inout [String : Any], AnyFieldInternal) in Record.toDictionary(appContext: AppContext?) + 16 17 libswiftCore.dylib 0x00000001920dd148 Sequence.reduce<A>(into: __owned A1, _: (inout A1, A.Element)) + 764 18 Quince 0x00000001044ba5e0 Record.toDictionary(appContext: AppContext?) + 136 19 Quince 0x00000001044f6820 VideoPlayer.safeEmit(event: String, payload: Record?) + 160 (VideoPlayer.swift:422) 20 Quince 0x00000001044f6fec VideoPlayer.currentVideoTrack.setter + 356 21 Quince 0x00000001044f87f8 protocol witness for VideoPlayerObserverDelegate.onVideoTrackChanged(player: AVPlayer, oldVideoTrack: VideoTrack?, newVideoTrack: VideoTrack?) in conformance VideoPlayer + 48 22 Quince 0x00000001044fe648 closure #1 (WeakPlayerObserverDelegate) in VideoPlayerObserver.currentVideoTrack.didset + 0 (VideoPlayerObserver.swift:87) 23 Quince 0x00000001044fe648 VideoPlayerObserver.currentVideoTrack.didset + 896 24 Quince 0x00000001044fe648 VideoPlayerObserver.currentVideoTrack.didset + 896 (VideoPlayerObserver.swift:86) 25 Quince 0x00000001045005a4 closure #1 () in closure #4 @sendable (AVPlayerItem, NSKeyValueObservedChange<[AVPlayerItemTrack]>) in VideoPlayerObserver.initializeCurrentPlayerItemObservers(player: AVPlayer, playerItem: AVPlayerItem) + 160 26 libswift_Concurrency.dylib 0x000000019336a8b4 swift::runJobInEstablishedExecutorContext(swift::Job*) + 288 27 libswift_Concurrency.dylib 0x000000019336bd28 swift_job_runImpl(swift::Job*, swift::SerialExecutorRef) + 156 28 libdispatch.dylib 0x00000001cdbc2f48 _dispatch_root_queue_drain + 364 29 libdispatch.dylib 0x00000001cdbc36fc _dispatch_worker_thread2 + 180 30 libsystem_pthread.dylib 0x00000001f19fd37c _pthread_wqthread + 232 31 libsystem_pthread.dylib 0x00000001f19fc8c0 start_wqthread + 8 ``` I managed to repro it (not the exact expo video crash but conditions under which it can happen) with an added testcase. The crash points to the `field.options` set call. <img width="300" height="auto" alt="Screenshot 2026-04-24 at 8 59 31 PM" src="https://github.com/user-attachments/assets/25a309fb-aaf5-4884-9c53-26343d86eda8" /> <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> # How Made `options` property private. Read and write needs to happen via `withOptions` function, which provides locked access to the `options` property <!-- How did you build this feature or fix this bug and why? --> # Test Plan Added a testcase for the crash. Run the tests with and without changes in Record.swift. When trying to repro the crash on test run it 2-3 times if not reproducible. <!-- Please describe how you tested this change and how a reviewer could reproduce your test, especially if this PR does not include automated tests! If possible, please also provide terminal output and/or screenshots demonstrating your test/reproduction. --> # Checklist <!-- Please check the appropriate items below if they apply to your diff. --> - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [x] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
# Why We should be using a consistent version of ESLint across the monorepo to make it easier for upgrades and eventually deprecating the old (non-flat) ESLint configurations in `eslint-config-universe`. # How Bumped the version of ESLint in `tools/` and migrated the ESLint configuration file to the new flat config style. After doing the above, fixed a handful of minor warnings that started showing up. Additionally, disabled the `import/no-named-as-default` in both the core and TypeScript presets as it doesn't seem to be a pattern we use in `expo/expo`. # Test Plan - CI # Checklist - [x] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting) - [x] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). - [ ] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )