Skip to content

Commit 2e9bc30

Browse files
authored
Fixes appWillTerminate not running on GDTStorage's queue (#4879)
* Fixes appWillTerminate not running on GDTStorage's queue Fixes #4546 Thanks @plu for the test case! * Use the correct lifecycle event. * Additional comments.
1 parent 30e1ff3 commit 2e9bc30

File tree

3 files changed

+41
-9
lines changed

3 files changed

+41
-9
lines changed

GoogleDataTransport/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# v4.0.1
2+
- Fixes missing a dispatch_sync and on-queue work in appWillTerminate of storage. (#4546)
3+
14
# v4.0.0
25
- Internal restructuring to support a single class implementing several backends.
36

GoogleDataTransport/GDTCORLibrary/GDTCORStorage.m

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -278,17 +278,19 @@ - (void)appWillBackground:(GDTCORApplication *)app {
278278
}
279279

280280
- (void)appWillTerminate:(GDTCORApplication *)application {
281-
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
282-
NSError *error;
283-
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
284-
requiringSecureCoding:YES
285-
error:&error];
286-
[data writeToFile:[GDTCORStorage archivePath] atomically:YES];
287-
} else {
281+
dispatch_sync(_storageQueue, ^{
282+
if (@available(macOS 10.13, iOS 11.0, tvOS 11.0, *)) {
283+
NSError *error;
284+
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self
285+
requiringSecureCoding:YES
286+
error:&error];
287+
[data writeToFile:[GDTCORStorage archivePath] atomically:YES];
288+
} else {
288289
#if !TARGET_OS_MACCATALYST && !TARGET_OS_WATCH
289-
[NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]];
290+
[NSKeyedArchiver archiveRootObject:self toFile:[GDTCORStorage archivePath]];
290291
#endif
291-
}
292+
}
293+
});
292294
}
293295

294296
#pragma mark - NSSecureCoding

GoogleDataTransport/GDTCORTests/Unit/GDTCORStorageTest.m

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,4 +442,31 @@ - (void)testQoSTierFast {
442442
});
443443
}
444444

445+
/** Fuzz tests the storing of events at the same time as a terminate lifecycle notification. This
446+
* test can fail if there's simultaneous access to ivars of GDTCORStorage with one access being
447+
* off the storage's queue. The terminate lifecycle event should operate on and flush the queue.
448+
*/
449+
- (void)testStoringEventsDuringTerminate {
450+
int numberOfIterations = 1000;
451+
for (int i = 0; i < numberOfIterations; i++) {
452+
NSString *testString = [NSString stringWithFormat:@"testString %d", i];
453+
GDTCOREvent *event = [[GDTCOREvent alloc] initWithMappingID:@"404" target:target];
454+
event.dataObjectTransportBytes = [testString dataUsingEncoding:NSUTF8StringEncoding];
455+
event.clockSnapshot = [GDTCORClock snapshot];
456+
XCTestExpectation *writtenExpectation = [self expectationWithDescription:@"event written"];
457+
XCTAssertNoThrow([[GDTCORStorage sharedInstance] storeEvent:event
458+
onComplete:^(BOOL wasWritten, NSError *error) {
459+
XCTAssertTrue(wasWritten);
460+
[writtenExpectation fulfill];
461+
}]);
462+
[self waitForExpectationsWithTimeout:10 handler:nil];
463+
if (i % 5 == 0) {
464+
[[GDTCORStorage sharedInstance] removeEvents:[GDTCORStorage sharedInstance].storedEvents.set];
465+
}
466+
[NSNotificationCenter.defaultCenter
467+
postNotificationName:kGDTCORApplicationWillTerminateNotification
468+
object:nil];
469+
}
470+
}
471+
445472
@end

0 commit comments

Comments
 (0)