Skip to content

Commit 9cb17fc

Browse files
authored
Fix #14273: Prevent race condition crash in FPRTraceBackgroundActivityTracker (#15382)
1 parent 94b1c76 commit 9cb17fc

File tree

3 files changed

+87
-9
lines changed

3 files changed

+87
-9
lines changed

FirebasePerformance/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Unreleased
2+
- [fixed] Prevent race condition crash in FPRTraceBackgroundActivityTracker. (#14273)
3+
14
# 12.3.0
25
- [fixed] Add missing nanopb dependency to fix SwiftPM builds when building
36
dynamically linked libraries. (#15276)

FirebasePerformance/Sources/AppActivity/FPRTraceBackgroundActivityTracker.m

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ @interface FPRTraceBackgroundActivityTracker ()
2323

2424
@property(nonatomic, readwrite) FPRTraceState traceBackgroundState;
2525

26+
- (void)registerNotificationObservers;
27+
2628
@end
2729

2830
@implementation FPRTraceBackgroundActivityTracker
@@ -35,21 +37,29 @@ - (instancetype)init {
3537
} else {
3638
_traceBackgroundState = FPRTraceStateForegroundOnly;
3739
}
40+
__weak typeof(self) weakSelf = self;
3841
dispatch_async(dispatch_get_main_queue(), ^{
39-
[[NSNotificationCenter defaultCenter] addObserver:self
40-
selector:@selector(applicationDidBecomeActive:)
41-
name:UIApplicationDidBecomeActiveNotification
42-
object:[UIApplication sharedApplication]];
43-
44-
[[NSNotificationCenter defaultCenter] addObserver:self
45-
selector:@selector(applicationDidEnterBackground:)
46-
name:UIApplicationDidEnterBackgroundNotification
47-
object:[UIApplication sharedApplication]];
42+
__strong typeof(weakSelf) strongSelf = weakSelf;
43+
if (strongSelf) {
44+
[strongSelf registerNotificationObservers];
45+
}
4846
});
4947
}
5048
return self;
5149
}
5250

51+
- (void)registerNotificationObservers {
52+
[[NSNotificationCenter defaultCenter] addObserver:self
53+
selector:@selector(applicationDidBecomeActive:)
54+
name:UIApplicationDidBecomeActiveNotification
55+
object:[UIApplication sharedApplication]];
56+
57+
[[NSNotificationCenter defaultCenter] addObserver:self
58+
selector:@selector(applicationDidEnterBackground:)
59+
name:UIApplicationDidEnterBackgroundNotification
60+
object:[UIApplication sharedApplication]];
61+
}
62+
5363
- (void)dealloc {
5464
// Remove all the notification observers registered.
5565
[[NSNotificationCenter defaultCenter] removeObserver:self];

FirebasePerformance/Tests/Unit/FPRTraceBackgroundActivityTrackerTest.m

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,69 @@ - (void)testBackgroundTracking {
6161
}];
6262
}
6363

64+
/** Tests that synchronous observer registration works correctly and observers are immediately
65+
* available. */
66+
- (void)testObservers_synchronousRegistrationAddsObserver {
67+
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
68+
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
69+
XCTAssertNotNil(tracker);
70+
71+
[notificationCenter postNotificationName:UIApplicationDidBecomeActiveNotification
72+
object:[UIApplication sharedApplication]];
73+
XCTAssertEqual(tracker.traceBackgroundState, FPRTraceStateForegroundOnly);
74+
75+
tracker = nil;
76+
XCTAssertNil(tracker);
77+
XCTAssertNoThrow([notificationCenter postNotificationName:UIApplicationDidBecomeActiveNotification
78+
object:[UIApplication sharedApplication]]);
79+
XCTAssertNoThrow([notificationCenter
80+
postNotificationName:UIApplicationDidEnterBackgroundNotification
81+
object:[UIApplication sharedApplication]]);
82+
}
83+
84+
/** Tests rapid creation and deallocation to verify race condition. */
85+
- (void)testRapidCreationAndDeallocation_noRaceCondition {
86+
for (int i = 0; i < 100; i++) {
87+
@autoreleasepool {
88+
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
89+
XCTAssertNotNil(tracker);
90+
91+
[[NSNotificationCenter defaultCenter]
92+
postNotificationName:UIApplicationDidBecomeActiveNotification
93+
object:[UIApplication sharedApplication]];
94+
}
95+
}
96+
97+
XCTAssertNoThrow([[NSNotificationCenter defaultCenter]
98+
postNotificationName:UIApplicationDidBecomeActiveNotification
99+
object:[UIApplication sharedApplication]]);
100+
XCTAssertNoThrow([[NSNotificationCenter defaultCenter]
101+
postNotificationName:UIApplicationDidEnterBackgroundNotification
102+
object:[UIApplication sharedApplication]]);
103+
}
104+
105+
/** Tests observer registration when created from background thread. */
106+
- (void)testObservers_registrationFromBackgroundThread {
107+
XCTestExpectation *expectation = [self expectationWithDescription:@"Background thread creation"];
108+
109+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
110+
FPRTraceBackgroundActivityTracker *tracker = [[FPRTraceBackgroundActivityTracker alloc] init];
111+
XCTAssertNotNil(tracker);
112+
113+
dispatch_async(dispatch_get_main_queue(), ^{
114+
[[NSNotificationCenter defaultCenter]
115+
postNotificationName:UIApplicationDidBecomeActiveNotification
116+
object:[UIApplication sharedApplication]];
117+
118+
XCTAssertEqual(tracker.traceBackgroundState, FPRTraceStateForegroundOnly);
119+
[expectation fulfill];
120+
});
121+
});
122+
123+
[self waitForExpectationsWithTimeout:5.0
124+
handler:^(NSError *error) {
125+
XCTAssertNil(error, @"Test timed out");
126+
}];
127+
}
128+
64129
@end

0 commit comments

Comments
 (0)