Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: crash when call SentryUIApplication in background thread #3855

Merged
merged 10 commits into from
Apr 18, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 24 additions & 4 deletions Sources/Sentry/SentryDispatchQueueWrapper.m
Original file line number Diff line number Diff line change
Expand Up @@ -56,23 +56,43 @@
}
}

- (nullable id)dispatchSyncOnMainQueueWithResult:(id (^)(void))block
{
return [self dispatchSyncOnMainQueueWithResult:block timeout:DISPATCH_TIME_FOREVER];

Check warning on line 61 in Sources/Sentry/SentryDispatchQueueWrapper.m

View check run for this annotation

Codecov / codecov/patch

Sources/Sentry/SentryDispatchQueueWrapper.m#L61

Added line #L61 was not covered by tests
}

- (BOOL)dispatchSyncOnMainQueue:(void (^)(void))block timeout:(NSTimeInterval)timeout
{
NSNumber *result = [self

Check warning on line 66 in Sources/Sentry/SentryDispatchQueueWrapper.m

View check run for this annotation

Codecov / codecov/patch

Sources/Sentry/SentryDispatchQueueWrapper.m#L66

Added line #L66 was not covered by tests
dispatchSyncOnMainQueueWithResult:^id _Nonnull {
block();
return @YES;

Check warning on line 69 in Sources/Sentry/SentryDispatchQueueWrapper.m

View check run for this annotation

Codecov / codecov/patch

Sources/Sentry/SentryDispatchQueueWrapper.m#L68-L69

Added lines #L68 - L69 were not covered by tests
}
timeout:timeout];
return result.boolValue;

Check warning on line 72 in Sources/Sentry/SentryDispatchQueueWrapper.m

View check run for this annotation

Codecov / codecov/patch

Sources/Sentry/SentryDispatchQueueWrapper.m#L71-L72

Added lines #L71 - L72 were not covered by tests
}

- (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
Expand Down
71 changes: 36 additions & 35 deletions Sources/Sentry/SentryUIApplication.m
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -63,29 +64,34 @@ - (UIApplication *)sharedApplication

- (NSArray<UIWindow *> *)windows
{
UIApplication *app = [self sharedApplication];
NSMutableArray *result = [NSMutableArray array];

if (@available(iOS 13.0, tvOS 13.0, *)) {
NSArray<UIScene *> *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<UIScene *> *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<UIApplicationDelegate> appDelegate = [self getApplicationDelegate:app];
id<UIApplicationDelegate> 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<UIViewController *> *)relevantViewControllers
Expand All @@ -109,23 +115,18 @@ - (UIApplication *)sharedApplication

- (nullable NSArray<NSString *> *)relevantViewControllersNames
{
__block NSArray<NSString *> *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<UIViewController *> *)relevantViewControllerFromWindow:(UIWindow *)window
Expand Down
5 changes: 5 additions & 0 deletions Sources/Sentry/include/SentryDispatchQueueWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading