Adopt OSAllocatedUnfairLock on iOS 16
vcsoares opened this issue · comments
I am currently working on a project that has seen a huge spike of production issues since iOS 16 got released a couple of days ago, and virtually all of them point to a call to os_unfair_lock_lock
made by ReactiveSwift as the cause of the crashes. One such example is included below for reference.
One of the changes introduced with iOS 16 is a new API for unfair locks, replacing os_unfair_lock
and its related methods. Considering ReactiveSwift is used in a large number of live apps, the library should adopt OSAllocatedUnfairLock from iOS 16 onwards to fix crashes like these and avoid other potential issues.
Crashed: com.apple.main-thread
0 libsystem_platform.dylib 0x608c _os_unfair_lock_recursive_abort + 36
1 libsystem_platform.dylib 0x898 _os_unfair_lock_lock_slow + 280
2 ReactiveSwift 0x3c0ac Signal.Core.send(_:) + 110 (Signal.swift:110)
3 ReactiveSwift 0x2cfe8 Signal.Observer.send(value:) + 105 (Observer.swift:105)
4 ReactiveSwift 0x3331c $defer #1 <A><A1>() in closure #1 in MutableProperty.modify<A>(_:) + 144
5 ReactiveSwift 0x32d10 MutableProperty.modify<A>(_:) + 128 (<compiler-generated>:128)
6 ReactiveSwift 0x32c68 MutableProperty.value.setter + 644 (Property.swift:644)
7 <REDACTED> 0x29d5b4 MapSearchViewModel.searchTextDidUpdate(with:) + 140 (MapSearchViewModel.swift:140)
8 <REDACTED> 0x2f292c MapSearchView.searchTextDidChange(_:) + 4304791852 (<compiler-generated>:4304791852)
9 <REDACTED> 0x2f2990 @objc MapSearchView.searchTextDidChange(_:) + 4304791952 (<compiler-generated>:4304791952)
10 UIKitCore 0x24f6f8 -[UIApplication sendAction:to:from:forEvent:] + 132
11 UIKitCore 0x24f3d8 -[UIControl sendAction:to:forEvent:] + 112
12 UIKitCore 0x24ed6c -[UIControl _sendActionsForEvents:withEvent:] + 324
13 UIKitCore 0x56a57c -[UITextField fieldEditorDidChange:] + 156
14 UIKitCore 0xf102b8 -[UIFieldEditor textInputDidChange:] + 84
15 UIKitCore 0x233968 -[UITextInputController _sendDelegateChangeNotificationsForText:selection:] + 152
16 UIKitCore 0xf271f4 -[UITextInputController replaceRangeWithTextWithoutClosingTyping:replacementText:] + 344
17 UIKitCore 0xf11db8 -[UIFieldEditor _handleObscuredTextInputIfNecessaryWithEditingBlock:] + 376
18 UIKitCore 0xf12428 -[UIFieldEditor replaceRangeWithTextWithoutClosingTyping:replacementText:] + 144
19 UIKitCore 0xf1c330 -[UITextField replaceRangeWithTextWithoutClosingTyping:replacementText:] + 76
20 UIKitCore 0x95d040 -[UIKBInputDelegateManager replaceRange:oldText:withText:forReplaceAction:] + 188
21 UIKitCore 0xba919c -[UIKeyboardImpl applyAutocorrection:] + 896
22 UIKitCore 0xb9cfe4 -[UIKeyboardImpl acceptAutocorrection:executionContextPassingTIKeyboardCandidate:] + 1172
23 UIKitCore 0xb9ba80 -[UIKeyboardImpl acceptAutocorrectionForWordTerminator:executionContextPassingTIKeyboardCandidate:] + 296
24 UIKitCore 0xb9b8d8 __56-[UIKeyboardImpl acceptAutocorrectionForWordTerminator:]_block_invoke + 140
25 UIKitCore 0x3d39b4 -[UIKeyboardTaskEntry execute:] + 208
26 UIKitCore 0x3d3898 -[UIKeyboardTaskQueue continueExecutionOnMainThread] + 304
27 UIKitCore 0xbc9914 -[UIKeyboardTaskQueue addAndReturnTask:] + 96
28 UIKitCore 0x50cdb8 -[UIKeyboardTaskQueue performSingleTask:] + 96
29 UIKitCore 0xb9b7d8 -[UIKeyboardImpl acceptAutocorrectionForWordTerminator:] + 208
30 UIKitCore 0xb9f830 -[UIKeyboardImpl _acceptAutocorrection] + 452
31 UIKitCore 0xb9f46c -[UIKeyboardImpl acceptAutocorrectionWithCompletionHandler:] + 296
32 UIKitCore 0xb83c5c -[UIKeyboardImpl setDelegate:force:fromBecomeFirstResponder:] + 2020
33 UIKitCore 0x8e165c -[UIKeyboardSceneDelegate _reloadInputViewsForKeyWindowSceneResponder:force:fromBecomeFirstResponder:] + 1752
34 UIKitCore 0x8e0f44 -[UIKeyboardSceneDelegate _reloadInputViewsForResponder:force:fromBecomeFirstResponder:] + 128
35 UIKitCore 0xd456ac -[UIResponder _finishResignFirstResponderFromBecomeFirstResponder:] + 328
36 UIKitCore 0x565334 -[UITextField _finishResignFirstResponder] + 56
37 UIKitCore 0x220060 -[UIResponder resignFirstResponder] + 284
38 UIKitCore 0x3c5090 -[UITextField resignFirstResponder] + 100
39 <REDACTED> 0x2f285c closure #4 in MapSearchView.setupReactiveBindings() + 4304791644
40 ReactiveSwift 0x2d328 closure #1 in Signal.Observer.init(value:failed:completed:interrupted:) + 60 (Observer.swift:60)
41 ReactiveSwift 0x2d444 partial apply for closure #1 in Signal.Observer.init(value:failed:completed:interrupted:) + 48 (<compiler-generated>:48)
42 ReactiveSwift 0x2d028 Signal.Observer.send(_:) + 97 (Observer.swift:97)
43 ReactiveSwift 0x3c14c Signal.Core.send(_:) + 480 (<compiler-generated>:480)
44 ReactiveSwift 0x2d528 closure #1 in Signal.Observer.init(mappingInterruptedToCompleted:) + 96 (Observer.swift:96)
45 ReactiveSwift 0x2d028 Signal.Observer.send(_:) + 97 (Observer.swift:97)
46 ReactiveSwift 0x3c14c Signal.Core.send(_:) + 480 (<compiler-generated>:480)
47 ReactiveSwift 0x2cfe8 Signal.Observer.send(value:) + 105 (Observer.swift:105)
48 ReactiveSwift 0x3331c $defer #1 <A><A1>() in closure #1 in MutableProperty.modify<A>(_:) + 144
49 ReactiveSwift 0x32d10 MutableProperty.modify<A>(_:) + 128 (<compiler-generated>:128)
50 ReactiveSwift 0x32c68 MutableProperty.value.setter + 644 (Property.swift:644)
51 <REDACTED> 0x29d48c MapSearchViewModel.didSelectRow(at:) + 123 (MapSearchViewModel.swift:123)
52 <REDACTED> 0x2f3410 @objc MapSearchView.tableView(_:didSelectRowAt:) + 4304794640 (<compiler-generated>:4304794640)
53 UIKitCore 0xe65e9c -[UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:deselectPrevious:performCustomSelectionAction:] + 1196
54 UIKitCore 0xe661b0 -[UITableView _userSelectRowAtPendingSelectionIndexPath:] + 256
55 UIKitCore 0x1a4958 -[_UIAfterCACommitBlock run] + 72
56 UIKitCore 0x1a488c -[_UIAfterCACommitQueue flush] + 176
57 UIKitCore 0x1a4798 _runAfterCACommitDeferredBlocks + 496
58 UIKitCore 0x3f6c0 _cleanUpAfterCAFlushAndRunDeferredBlocks + 108
59 UIKitCore 0x504fc4 _UIApplicationFlushCATransaction + 72
60 UIKitCore 0x651678 _UIUpdateSequenceRun + 84
61 UIKitCore 0xc90904 schedulerStepScheduledMainSection + 172
62 UIKitCore 0xc8fad0 runloopSourceCallback + 92
63 CoreFoundation 0xd622c __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 28
64 CoreFoundation 0xe2614 __CFRunLoopDoSource0 + 176
65 CoreFoundation 0x6651c __CFRunLoopDoSources0 + 244
66 CoreFoundation 0x7beb8 __CFRunLoopRun + 836
67 CoreFoundation 0x811e4 CFRunLoopRunSpecific + 612
68 GraphicsServices 0x1368 GSEventRunModal + 164
69 UIKitCore 0x3a2d88 -[UIApplication _run] + 888
70 UIKitCore 0x3a29ec UIApplicationMain + 340
71 <REDACTED> 0x744c main + 17 (AppDelegate.swift:17)
72 ??? 0x1bbabd948 (Ausente)
Are you modifying a property recursively by any chance?
Not that I'm aware of, no. These crashes have never happened on iOS 15 and earlier.
Either I agree using OSAllocatedUnfairLock
on iOS 16 and forward would be nice 👍🏻
I also see some crashes in iOS 16 due to same problem.
I ran into the same crash but it was due to modifying a property recursively.
From the documentation of OSAllocatedUnfairLock
it says:
Important
If you have existing Swift code that uses os_unfair_lock, change it to use OSAllocatedUnfairLock to ensure correct locking behavior.
So to support iOS 16+ (and macOS, watchOS etc of the same era) use of os_unfair_lock
should be changed to OSAllocatedUnfairLock
.
Note that OSAllocatedUnfairLock
is not recursive:
Warning
OSAllocatedUnfairLock isn’t a recursive lock. Attempting to lock an object more than once from the same thread without unlocking in between triggers a runtime exception.
But that shouldn't be an issue as os_unfair_lock
doesn't allow recursion either.
Yeah I'm all for replacing the implementation starting with iOS 16 👍🏻
I can't reproduce the crash but i've tested the change and i can't find any side effect.