Skip to content

Commit 6f2fa46

Browse files
authored
[e-m-c] fix queue assertion crash (expo#41296)
# Why Ran into an issue with background tasks (triggered by a background notification): <details> <summary>Details</summary> ``` (lldb) bt * thread #19, queue = 'expo.application.remoteNotification', stop reason = EXC_BREAKPOINT (code=1, subcode=0x1038578e4) frame #0: 0x00000001038578e4 libdispatch.dylib`_dispatch_assert_queue_fail + 120 frame #1: 0x000000010388e01c libdispatch.dylib`dispatch_assert_queue$V2.cold.1 + 116 frame #2: 0x0000000103857868 libdispatch.dylib`dispatch_assert_queue + 108 frame #3: 0x000000018996003c libswift_Concurrency.dylib`_swift_task_checkIsolatedSwift + 48 frame #4: 0x00000001899bf724 libswift_Concurrency.dylib`swift_task_isCurrentExecutorWithFlagsImpl(swift::SerialExecutorRef, swift::swift_task_is_current_executor_flag) + 356 frame #5: 0x00000001050e6678 notificationtester.debug.dylib`closure #1 in closure #2 in static ExpoAppDelegateSubscriberManager.application(result=.noData, failedCount=0, newDataCount=0, subscribersLeft=2, completionHandler=0x0000000104f6dea8 notificationtester.debug.dylib`partial apply forwarder for reabstraction thunk helper from @escaping @callee_unowned @convention(block) (@unowned __C.UIBackgroundFetchResult) -> () to @escaping @callee_guaranteed (@unowned __C.UIBackgroundFetchResult) -> () at <compiler-generated>) at ExpoAppDelegateSubscriberManager.swift:0 frame #8: 0x000000010386e2e0 libdispatch.dylib`_dispatch_client_callout + 16 frame #9: 0x00000001038653cc libdispatch.dylib`_dispatch_lane_barrier_sync_invoke_and_complete + 172 * frame #10: 0x00000001050e650c notificationtester.debug.dylib`closure #2 in static ExpoAppDelegateSubscriberManager.application(result=.noData, dispatchQueue=0x000000012c455d80, failedCount=0, newDataCount=0, subscribersLeft=2, completionHandler=0x0000000104f6dea8 notificationtester.debug.dylib`partial apply forwarder for reabstraction thunk helper from @escaping @callee_unowned @convention(block) (@unowned __C.UIBackgroundFetchResult) -> () to @escaping @callee_guaranteed (@unowned __C.UIBackgroundFetchResult) -> () at <compiler-generated>) at ExpoAppDelegateSubscriberManager.swift:225:21 frame #12: 0x0000000104f6bad8 notificationtester.debug.dylib`__94-[EXLegacyAppDelegateWrapper application:didReceiveRemoteNotification:fetchCompletionHandler:]_block_invoke(.block_descriptor=0x0000000120206340, result=UIBackgroundFetchResultNoData) at EXLegacyAppDelegateWrapper.m:212:9 frame #13: 0x0000000104f65064 notificationtester.debug.dylib`__63-[EXTaskService runTasksWithReason:userInfo:completionHandler:]_block_invoke(.block_descriptor=0x000000012bbebc90, results=@"1 element") at EXTaskService.m:351:7 frame #14: 0x0000000104f666f8 notificationtester.debug.dylib`__67-[EXTaskService _runTasksSupportingLaunchReason:userInfo:callback:]_block_invoke(.block_descriptor=0x0000000120204f80, results=@"1 element") at EXTaskService.m:517:7 frame #15: 0x0000000104f60cdc notificationtester.debug.dylib`-[EXTaskExecutionRequest _maybeExecuteCallback](self=0x000000012c519b60, _cmd="_maybeExecuteCallback") at EXTaskExecutionRequest.m:59:5 frame #16: 0x0000000104f60c74 notificationtester.debug.dylib`-[EXTaskExecutionRequest maybeEvaluate](self=0x000000012c519b60, _cmd="maybeEvaluate") at EXTaskExecutionRequest.m:46:5 frame #17: 0x0000000104f60b98 notificationtester.debug.dylib`-[EXTaskExecutionRequest task:didFinishWithResult:](self=0x000000012c519b60, _cmd="task:didFinishWithResult:", task=0x0000000120370840, result=(long)1) at EXTaskExecutionRequest.m:35:3 frame #18: 0x0000000104f63e64 notificationtester.debug.dylib`-[EXTaskService notifyTaskWithName:forAppId:didFinishWithResponse:](self=0x00000001200014c0, _cmd="notifyTaskWithName:forAppId:didFinishWithResponse:", taskName=@"BACKGROUND_NOTIFICATION_TASK", appId=@"mainApplication", response=1 key/value pair) at EXTaskService.m:217:7 frame #19: 0x0000000104f617c4 notificationtester.debug.dylib`-[EXTaskManager notifyTaskFinished:withResponse:resolve:reject:](self=0x0000000120371600, _cmd="notifyTaskFinished:withResponse:resolve:reject:", taskName=@"BACKGROUND_NOTIFICATION_TASK", response=1 key/value pair, resolve=0x000000010ad20e7c, reject=0x000000010ad213bc) at EXTaskManager.m:104:3 frame #20: 0x000000018b4ee6c4 CoreFoundation`__invoking___ + 148 frame #21: 0x000000018b4ee550 CoreFoundation`-[NSInvocation invoke] + 424 frame #22: 0x0000000105015b5c notificationtester.debug.dylib`-[EXExportedModule callExportedMethod:withArguments:resolver:rejecter:](self=0x0000000120371600, _cmd="callExportedMethod:withArguments:resolver:rejecter:", methodName=@"notifyTaskFinishedAsync", arguments=@"2 elements", resolve=0x000000010ad20e7c, reject=0x000000010ad213bc) at EXExportedModule.m:168:3 frame #23: 0x000000010501fa10 notificationtester.debug.dylib`__79-[EXNativeModulesProxy callMethod:methodNameOrKey:arguments:resolver:rejecter:]_block_invoke(.block_descriptor=0x0000000122ea9270) at EXNativeModulesProxy.mm:243:7 frame #24: 0x000000010385463c libdispatch.dylib`_dispatch_call_block_and_release + 32 frame #25: 0x000000010386e2e0 libdispatch.dylib`_dispatch_client_callout + 16 frame expo#26: 0x000000010385cb4c libdispatch.dylib`_dispatch_lane_serial_drain + 796 frame expo#27: 0x000000010385d7d4 libdispatch.dylib`_dispatch_lane_invoke + 432 frame expo#28: 0x0000000103869b20 libdispatch.dylib`_dispatch_root_queue_drain_deferred_wlh + 344 frame expo#29: 0x00000001038691c4 libdispatch.dylib`_dispatch_workloop_worker_thread + 752 frame expo#30: 0x00000001e7d803b8 libsystem_pthread.dylib`_pthread_wqthread + 292 ``` </details> # How - dispatch the completion handlers on main queue # Test Plan - tested locally with notification-tester app # 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) - [ ] 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)
1 parent 4cf4ae0 commit 6f2fa46

File tree

2 files changed

+41
-44
lines changed

2 files changed

+41
-44
lines changed

packages/expo-modules-core/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
### 🐛 Bug fixes
2929

3030
- [iOS] Fix throwing `InvalidArgsNumberException` when declaring `AsyncFunction` with optional arguments and `Promise`. ([#41054](https://github.com/expo/expo/pull/41054) by [@Wenszel](https://github.com/Wenszel))
31+
- [iOS] fix queue assertion crash ([#41296](https://github.com/expo/expo/pull/41296) by [@vonovak](https://github.com/vonovak))
3132
- [core] [iOS] Addresses a potential crash where `[NSString UTF8String]` could return `nil` for certain string inputs (non-English), leading to an attempt to dereference a null pointer during `jsi::String` creation. ([#40639](https://github.com/expo/expo/pull/40639) by [@mohammadamin16](https://github.com/mohammadamin16))
3233
- [iOS] Fix `Either` conversion in `Record` ([#40655](https://github.com/expo/expo/pull/40655) by [@vonovak](https://github.com/vonovak))
3334
- [RSC] Fix server components asserting from missing native modules. ([#40388](https://github.com/expo/expo/pull/40388) by [@EvanBacon](https://github.com/EvanBacon))

packages/expo-modules-core/ios/AppDelegates/ExpoAppDelegateSubscriberManager.swift

Lines changed: 40 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,14 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
167167
) {
168168
let selector = #selector(application(_:handleEventsForBackgroundURLSession:completionHandler:))
169169
let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
170+
if subs.isEmpty {
171+
completionHandler()
172+
return
173+
}
170174
var subscribersLeft = subs.count
171-
let dispatchQueue = DispatchQueue(label: "expo.application.handleBackgroundEvents")
172175

173-
let handler = {
174-
dispatchQueue.sync {
176+
let aggregatedHandler = {
177+
DispatchQueue.main.async {
175178
subscribersLeft -= 1
176179

177180
if subscribersLeft == 0 {
@@ -180,12 +183,8 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
180183
}
181184
}
182185

183-
if subs.isEmpty {
184-
completionHandler()
185-
} else {
186-
subs.forEach {
187-
$0.application?(application, handleEventsForBackgroundURLSession: identifier, completionHandler: handler)
188-
}
186+
subs.forEach {
187+
$0.application?(application, handleEventsForBackgroundURLSession: identifier, completionHandler: aggregatedHandler)
189188
}
190189
}
191190

@@ -216,13 +215,17 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
216215
) {
217216
let selector = #selector(application(_:didReceiveRemoteNotification:fetchCompletionHandler:))
218217
let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
218+
if subs.isEmpty {
219+
completionHandler(.noData)
220+
return
221+
}
222+
219223
var subscribersLeft = subs.count
220-
let dispatchQueue = DispatchQueue(label: "expo.application.remoteNotification", qos: .userInteractive)
221224
var failedCount = 0
222225
var newDataCount = 0
223226

224-
let handler = { (result: UIBackgroundFetchResult) in
225-
dispatchQueue.sync {
227+
let aggregatedHandler = { (result: UIBackgroundFetchResult) in
228+
DispatchQueue.main.async {
226229
if result == .failed {
227230
failedCount += 1
228231
} else if result == .newData {
@@ -243,12 +246,8 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
243246
}
244247
}
245248

246-
if subs.isEmpty {
247-
completionHandler(.noData)
248-
} else {
249-
subs.forEach { subscriber in
250-
subscriber.application?(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: handler)
251-
}
249+
subs.forEach { subscriber in
250+
subscriber.application?(application, didReceiveRemoteNotification: userInfo, fetchCompletionHandler: aggregatedHandler)
252251
}
253252
}
254253

@@ -288,11 +287,10 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
288287
let selector = #selector(application(_:continue:restorationHandler:))
289288
let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
290289
var subscribersLeft = subs.count
291-
let dispatchQueue = DispatchQueue(label: "expo.application.continueUserActivity", qos: .userInteractive)
292290
var allRestorableObjects = [UIUserActivityRestoring]()
293291

294-
let handler = { (restorableObjects: [UIUserActivityRestoring]?) in
295-
dispatchQueue.sync {
292+
let aggregatedHandler = { (restorableObjects: [UIUserActivityRestoring]?) in
293+
DispatchQueue.main.async {
296294
if let restorableObjects = restorableObjects {
297295
allRestorableObjects.append(contentsOf: restorableObjects)
298296
}
@@ -306,7 +304,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
306304
}
307305

308306
return subs.reduce(false) { result, subscriber in
309-
return subscriber.application?(application, continue: userActivity, restorationHandler: handler) ?? false || result
307+
return subscriber.application?(application, continue: userActivity, restorationHandler: aggregatedHandler) ?? false || result
310308
}
311309
}
312310
#elseif os(macOS)
@@ -319,11 +317,10 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
319317
let selector = #selector(application(_:continue:restorationHandler:))
320318
let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
321319
var subscribersLeft = subs.count
322-
let dispatchQueue = DispatchQueue(label: "expo.application.continueUserActivity", qos: .userInteractive)
323320
var allRestorableObjects = [NSUserActivityRestoring]()
324321

325-
let handler = { (restorableObjects: [NSUserActivityRestoring]?) in
326-
dispatchQueue.sync {
322+
let aggregatedHandler = { (restorableObjects: [NSUserActivityRestoring]?) in
323+
DispatchQueue.main.async {
327324
if let restorableObjects = restorableObjects {
328325
allRestorableObjects.append(contentsOf: restorableObjects)
329326
}
@@ -337,7 +334,7 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
337334
}
338335

339336
return subs.reduce(false) { result, subscriber in
340-
return subscriber.application?(application, continue: userActivity, restorationHandler: handler) ?? false || result
337+
return subscriber.application?(application, continue: userActivity, restorationHandler: aggregatedHandler) ?? false || result
341338
}
342339
}
343340
#endif
@@ -369,10 +366,14 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
369366
let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
370367
var subscribersLeft = subs.count
371368
var result: Bool = false
372-
let dispatchQueue = DispatchQueue(label: "expo.application.performAction", qos: .userInteractive)
373369

374-
let handler = { (succeeded: Bool) in
375-
dispatchQueue.sync {
370+
if subs.isEmpty {
371+
completionHandler(result)
372+
return
373+
}
374+
375+
let aggregatedHandler = { (succeeded: Bool) in
376+
DispatchQueue.main.async {
376377
result = result || succeeded
377378
subscribersLeft -= 1
378379

@@ -382,12 +383,8 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
382383
}
383384
}
384385

385-
if subs.isEmpty {
386-
completionHandler(result)
387-
} else {
388-
subs.forEach { subscriber in
389-
subscriber.application?(application, performActionFor: shortcutItem, completionHandler: handler)
390-
}
386+
subs.forEach { subscriber in
387+
subscriber.application?(application, performActionFor: shortcutItem, completionHandler: aggregatedHandler)
391388
}
392389
}
393390
#endif
@@ -403,12 +400,15 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
403400
let selector = #selector(application(_:performFetchWithCompletionHandler:))
404401
let subs = ExpoAppDelegateSubscriberRepository.subscribers.filter { $0.responds(to: selector) }
405402
var subscribersLeft = subs.count
406-
let dispatchQueue = DispatchQueue(label: "expo.application.performFetch", qos: .userInteractive)
403+
if subs.isEmpty {
404+
completionHandler(.noData)
405+
return
406+
}
407407
var failedCount = 0
408408
var newDataCount = 0
409409

410-
let handler = { (result: UIBackgroundFetchResult) in
411-
dispatchQueue.sync {
410+
let aggregatedHandler = { (result: UIBackgroundFetchResult) in
411+
DispatchQueue.main.async {
412412
if result == .failed {
413413
failedCount += 1
414414
} else if result == .newData {
@@ -429,12 +429,8 @@ public class ExpoAppDelegateSubscriberManager: NSObject {
429429
}
430430
}
431431

432-
if subs.isEmpty {
433-
completionHandler(.noData)
434-
} else {
435-
subs.forEach { subscriber in
436-
subscriber.application?(application, performFetchWithCompletionHandler: handler)
437-
}
432+
subs.forEach { subscriber in
433+
subscriber.application?(application, performFetchWithCompletionHandler: aggregatedHandler)
438434
}
439435
}
440436

0 commit comments

Comments
 (0)