From 3f6c30b04dc2377110490170c382b92ae85ee9a6 Mon Sep 17 00:00:00 2001 From: Dhiogo Brustolin Date: Wed, 4 Oct 2023 14:00:28 +0200 Subject: [PATCH] fix: App hang with race condition for tick counter (#3290) Thread sanitizer was complaining about a race condition in the ANR tracker. Added atomic operations to avoid the race condition --- CHANGELOG.md | 1 + Sources/Sentry/SentryANRTracker.m | 10 ++++++---- Tests/SentryTests/PrivateSentrySDKOnlyTests.swift | 4 ++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1b9583ae28..7a9fb22abe9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- App hang with race condition for tick counter (#3290) - Remove "duplicate library" warning (#3312) ## 8.13.0 diff --git a/Sources/Sentry/SentryANRTracker.m b/Sources/Sentry/SentryANRTracker.m index 6d8d778cf00..29dba69d03e 100644 --- a/Sources/Sentry/SentryANRTracker.m +++ b/Sources/Sentry/SentryANRTracker.m @@ -5,6 +5,7 @@ #import "SentryDispatchQueueWrapper.h" #import "SentryLog.h" #import "SentryThreadWrapper.h" +#import NS_ASSUME_NONNULL_BEGIN @@ -64,7 +65,7 @@ - (void)detectANRs state = kSentryANRTrackerRunning; } - __block NSInteger ticksSinceUiUpdate = 0; + __block atomic_int ticksSinceUiUpdate = 0; __block BOOL reported = NO; NSInteger reportThreshold = 5; @@ -81,10 +82,10 @@ - (void)detectANRs NSDate *blockDeadline = [[SentryDependencyContainer.sharedInstance.dateProvider date] dateByAddingTimeInterval:self.timeoutInterval]; - ticksSinceUiUpdate++; + atomic_fetch_add_explicit(&ticksSinceUiUpdate, 1, memory_order_relaxed); [self.dispatchQueueWrapper dispatchAsyncOnMainQueue:^{ - ticksSinceUiUpdate = 0; + atomic_store_explicit(&ticksSinceUiUpdate, 0, memory_order_relaxed); if (reported) { SENTRY_LOG_WARN(@"ANR stopped."); @@ -115,7 +116,8 @@ - (void)detectANRs continue; } - if (ticksSinceUiUpdate >= reportThreshold && !reported) { + if (atomic_load_explicit(&ticksSinceUiUpdate, memory_order_relaxed) >= reportThreshold + && !reported) { reported = YES; if (![self.crashWrapper isApplicationInForeground]) { diff --git a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift index 908cfc3d4e3..5a2574a3546 100644 --- a/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift +++ b/Tests/SentryTests/PrivateSentrySDKOnlyTests.swift @@ -124,7 +124,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { /** * Smoke Tests profiling via PrivateSentrySDKOnly. Actual profiling unit tests are done elsewhere. */ - func testProfilingStartAndCollect() { + func testProfilingStartAndCollect() throws { let options = Options() options.dsn = TestConstants.dsnAsString(username: "SentryFramesTrackingIntegrationTests") let client = TestClient(options: options) @@ -148,7 +148,7 @@ class PrivateSentrySDKOnlyTests: XCTestCase { XCTAssertNotNil(profile?["frames"]) let transactionInfo = payload?["transaction"] as? NSDictionary XCTAssertNotNil(transactionInfo) - XCTAssertGreaterThan(transactionInfo?["active_thread_id"] as! Int64, 0) + XCTAssertGreaterThan(try XCTUnwrap(transactionInfo?["active_thread_id"] as? Int64), 0) } func testProfilingDiscard() {