From 953e914d3c154ff0a96298b980fa09f71a062678 Mon Sep 17 00:00:00 2001 From: mlch911 Date: Thu, 18 Apr 2024 23:18:12 +0800 Subject: [PATCH] fix: crash when call SentryUIApplication in background thread (#3855) Fix crash when call SentryUIApplication in background thread. #3836 Co-authored-by: Dhiogo Brustolin --- CHANGELOG.md | 1 + Sources/Sentry/SentryDispatchQueueWrapper.m | 28 ++++++-- Sources/Sentry/SentryUIApplication.m | 71 ++++++++++--------- .../include/SentryDispatchQueueWrapper.h | 5 ++ 4 files changed, 66 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 461f681228..0e02e70b01 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ ### Fixes +- Crash due to a background call to -[UIApplication applicationState] (#3855) - Save framework without UIKit/AppKit as Github Asset for releases (#3858) ## 8.24.0 diff --git a/Sources/Sentry/SentryDispatchQueueWrapper.m b/Sources/Sentry/SentryDispatchQueueWrapper.m index e092e4f130..b238ee349d 100644 --- a/Sources/Sentry/SentryDispatchQueueWrapper.m +++ b/Sources/Sentry/SentryDispatchQueueWrapper.m @@ -56,23 +56,43 @@ - (void)dispatchSyncOnMainQueue:(void (^)(void))block } } +- (nullable id)dispatchSyncOnMainQueueWithResult:(id (^)(void))block +{ + return [self dispatchSyncOnMainQueueWithResult:block timeout:DISPATCH_TIME_FOREVER]; +} + - (BOOL)dispatchSyncOnMainQueue:(void (^)(void))block timeout:(NSTimeInterval)timeout +{ + NSNumber *result = [self + dispatchSyncOnMainQueueWithResult:^id _Nonnull { + block(); + return @YES; + } + timeout:timeout]; + return result.boolValue; +} + +- (nullable id)dispatchSyncOnMainQueueWithResult:(id (^)(void))block timeout:(NSTimeInterval)timeout { if ([NSThread isMainThread]) { - block(); + return block(); } else { dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); + __block id result; dispatch_async(dispatch_get_main_queue(), ^{ - block(); + result = block(); dispatch_semaphore_signal(semaphore); }); dispatch_time_t timeout_t = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)); - return dispatch_semaphore_wait(semaphore, timeout_t) == 0; + if (dispatch_semaphore_wait(semaphore, timeout_t) == 0) { + return result; + } else { + return nil; + } } - return YES; } - (void)dispatchAfter:(NSTimeInterval)interval block:(dispatch_block_t)block diff --git a/Sources/Sentry/SentryUIApplication.m b/Sources/Sentry/SentryUIApplication.m index d6eb19a224..bd1ca3bbbe 100644 --- a/Sources/Sentry/SentryUIApplication.m +++ b/Sources/Sentry/SentryUIApplication.m @@ -28,7 +28,8 @@ - (instancetype)init // We store the application state when the app is initialized // and we keep track of its changes by the notifications // this way we avoid calling sharedApplication in a background thread - appState = self.sharedApplication.applicationState; + [SentryDependencyContainer.sharedInstance.dispatchQueueWrapper + dispatchOnMainQueue:^{ self->appState = self.sharedApplication.applicationState; }]; } return self; } @@ -63,29 +64,34 @@ - (UIApplication *)sharedApplication - (NSArray *)windows { - UIApplication *app = [self sharedApplication]; - NSMutableArray *result = [NSMutableArray array]; - - if (@available(iOS 13.0, tvOS 13.0, *)) { - NSArray *scenes = [self getApplicationConnectedScenes:app]; - for (UIScene *scene in scenes) { - if (scene.activationState == UISceneActivationStateForegroundActive && scene.delegate && - [scene.delegate respondsToSelector:@selector(window)]) { - id window = [scene.delegate performSelector:@selector(window)]; - if (window) { - [result addObject:window]; + return [SentryDependencyContainer.sharedInstance.dispatchQueueWrapper + dispatchSyncOnMainQueueWithResult:^id _Nonnull { + UIApplication *app = [self sharedApplication]; + NSMutableArray *result = [NSMutableArray array]; + + if (@available(iOS 13.0, tvOS 13.0, *)) { + NSArray *scenes = [self getApplicationConnectedScenes:app]; + for (UIScene *scene in scenes) { + if (scene.activationState == UISceneActivationStateForegroundActive + && scene.delegate && + [scene.delegate respondsToSelector:@selector(window)]) { + id window = [scene.delegate performSelector:@selector(window)]; + if (window) { + [result addObject:window]; + } + } } } - } - } - id appDelegate = [self getApplicationDelegate:app]; + id appDelegate = [self getApplicationDelegate:app]; - if ([appDelegate respondsToSelector:@selector(window)] && appDelegate.window != nil) { - [result addObject:appDelegate.window]; - } + if ([appDelegate respondsToSelector:@selector(window)] && appDelegate.window != nil) { + [result addObject:appDelegate.window]; + } - return result; + return result; + } + timeout:0.01]; } - (NSArray *)relevantViewControllers @@ -109,23 +115,18 @@ - (UIApplication *)sharedApplication - (nullable NSArray *)relevantViewControllersNames { - __block NSArray *result = nil; - - void (^addViewNames)(void) = ^{ - NSArray *viewControllers - = SentryDependencyContainer.sharedInstance.application.relevantViewControllers; - NSMutableArray *vcsNames = [[NSMutableArray alloc] initWithCapacity:viewControllers.count]; - for (id vc in viewControllers) { - [vcsNames addObject:[SwiftDescriptor getObjectClassName:vc]]; + return [SentryDependencyContainer.sharedInstance.dispatchQueueWrapper + dispatchSyncOnMainQueueWithResult:^id _Nonnull { + NSArray *viewControllers + = SentryDependencyContainer.sharedInstance.application.relevantViewControllers; + NSMutableArray *vcsNames = + [[NSMutableArray alloc] initWithCapacity:viewControllers.count]; + for (id vc in viewControllers) { + [vcsNames addObject:[SwiftDescriptor getObjectClassName:vc]]; + } + return [NSArray arrayWithArray:vcsNames]; } - result = [NSArray arrayWithArray:vcsNames]; - }; - - [[SentryDependencyContainer.sharedInstance dispatchQueueWrapper] - dispatchSyncOnMainQueue:addViewNames - timeout:0.01]; - - return result; + timeout:0.01]; } - (NSArray *)relevantViewControllerFromWindow:(UIWindow *)window diff --git a/Sources/Sentry/include/SentryDispatchQueueWrapper.h b/Sources/Sentry/include/SentryDispatchQueueWrapper.h index 61d9361af9..4a91f36989 100644 --- a/Sources/Sentry/include/SentryDispatchQueueWrapper.h +++ b/Sources/Sentry/include/SentryDispatchQueueWrapper.h @@ -19,8 +19,13 @@ NS_ASSUME_NONNULL_BEGIN - (void)dispatchSyncOnMainQueue:(void (^)(void))block; +- (nullable id)dispatchSyncOnMainQueueWithResult:(id (^)(void))block; + - (BOOL)dispatchSyncOnMainQueue:(void (^)(void))block timeout:(NSTimeInterval)timeout; +- (nullable id)dispatchSyncOnMainQueueWithResult:(id (^)(void))block + timeout:(NSTimeInterval)timeout; + - (void)dispatchAfter:(NSTimeInterval)interval block:(dispatch_block_t)block; - (void)dispatchCancel:(dispatch_block_t)block;