Skip to content

Commit 295c3f6

Browse files
committed
Ensure stopped callback happen on service delayed fetches.
Also update/enable tests to check/match this.
1 parent 7c579d3 commit 295c3f6

File tree

2 files changed

+48
-13
lines changed

2 files changed

+48
-13
lines changed

Sources/Core/GTMSessionFetcher.m

+40-8
Original file line numberDiff line numberDiff line change
@@ -706,11 +706,16 @@ - (void)beginFetchMayDelay:(BOOL)mayDelay
706706
mayDelay = NO;
707707
}
708708
if (mayDelay && _service) {
709-
// Set the delayed state so there can't be a race incase between it getting queued in
710-
// the `fetcherShouldBeginFetching:` call and some other thread completing a different
711-
// fetch and thus starting this one (if we were trying to set the state based on the
712-
// return result).
713-
@synchronized(self) { _delayState = kDelayStateServiceDelayed; }
709+
BOOL savedStoppedState;
710+
@synchronized(self) {
711+
savedStoppedState = _userStoppedFetching;
712+
// Set the delayed state so there can't be a race incase between it getting queued in
713+
// the `fetcherShouldBeginFetching:` call and some other thread completing a different
714+
// fetch and thus starting this one. If we were trying to set the state based on the
715+
// return result, there would be a small window for that race.
716+
_delayState = kDelayStateServiceDelayed;
717+
}
718+
714719
BOOL shouldFetchNow = [_service fetcherShouldBeginFetching:self];
715720
if (!shouldFetchNow) {
716721
// The fetch is deferred, but will happen later.
@@ -726,8 +731,21 @@ - (void)beginFetchMayDelay:(BOOL)mayDelay
726731
}
727732
return;
728733
}
729-
// Per comment above, correct state since it wasn't delayed.
730-
@synchronized(self) { _delayState = kDelayStateNotDelayed; }
734+
735+
@synchronized(self) {
736+
// Per comment above, correct state since it wasn't delayed.
737+
_delayState = kDelayStateNotDelayed;
738+
739+
// If a `-stopFetching` came in while the service check was made, then the side effect of the
740+
// state setting caused the handler (if needed) to already be made, so there we want to just
741+
// exit and not continue the fetch.
742+
//
743+
// TODO(thomasvl): If `savedStoppedState` was already `YES`, then it's a more general problem
744+
// which will be handled elsewhere/later.
745+
if (!savedStoppedState && savedStoppedState != _userStoppedFetching) {
746+
return;
747+
}
748+
}
731749
}
732750

733751
if ([fetchRequest valueForHTTPHeaderField:@"User-Agent"] == nil) {
@@ -2051,14 +2069,28 @@ - (void)forgetSessionIdentifierForFetcherWithoutSyncCheck {
20512069

20522070
// External stop method
20532071
- (void)stopFetching {
2072+
BOOL triggerCallback;
20542073
@synchronized(self) {
20552074
GTMSessionMonitorSynchronized(self);
20562075

20572076
// Prevent enqueued callbacks from executing. The completion handler will still execute if
20582077
// the property `stopFetchingTriggersCompletionHandler` is `YES`.
20592078
_userStoppedFetching = YES;
2079+
2080+
// `stopFetchReleasingCallbacks:` will dequeue it if there is a sevice throttled
2081+
// delay, so the canceled callback needs to be directly triggered since the serivce
2082+
// won't attempt to restart it.
2083+
triggerCallback = _delayState == kDelayStateServiceDelayed && self.stopFetchingTriggersCompletionHandler;
20602084
} // @synchronized(self)
2061-
[self stopFetchReleasingCallbacks:!self.stopFetchingTriggersCompletionHandler];
2085+
2086+
if (triggerCallback) {
2087+
NSError *error = [NSError errorWithDomain:kGTMSessionFetcherErrorDomain
2088+
code:GTMSessionFetcherErrorUserCancelled
2089+
userInfo:nil];
2090+
[self finishWithError:error shouldRetry:NO];
2091+
} else {
2092+
[self stopFetchReleasingCallbacks:!self.stopFetchingTriggersCompletionHandler];
2093+
}
20622094
}
20632095

20642096
// Cancel the fetch of the URL that's currently in progress.

UnitTests/GTMSessionFetcherServiceTest.m

+8-5
Original file line numberDiff line numberDiff line change
@@ -346,11 +346,9 @@ - (void)testFetcherService {
346346
for (int idx = 1; idx <= 5; idx++) [urlArray addObject:validFileURL];
347347
for (int idx = 1; idx <= 3; idx++) [urlArray addObject:stopSilentFileURL];
348348
for (int idx = 1; idx <= 5; idx++) [urlArray addObject:altValidURL];
349-
// TODO(thomasvl) currently fail: The current code paths remove these from
350-
// pending, but doesn't actually get the completions ever called as requested.
351-
// Will be fixed in follow up PRs.
352-
// for (int idx = 1; idx <= 3; idx++) [urlArray addObject:stopCallbackFileURL];
349+
for (int idx = 1; idx <= 3; idx++) [urlArray addObject:stopCallbackFileURL];
353350
for (int idx = 1; idx <= 5; idx++) [urlArray addObject:validFileURL];
351+
for (int idx = 1; idx <= 2; idx++) [urlArray addObject:stopCallbackFileURL];
354352
for (int idx = 1; idx <= 2; idx++) [urlArray addObject:stopSilentFileURL];
355353
NSUInteger totalNumberOfFetchers = urlArray.count;
356354

@@ -572,6 +570,10 @@ - (void)testStopAllFetchersSeparately {
572570
[self stopAllFetchersHelperUseStopAllAPI:NO callbacksAfterStop:NO];
573571
}
574572

573+
- (void)testStopAllFetchersWithCallbacks {
574+
[self stopAllFetchersHelperUseStopAllAPI:YES callbacksAfterStop:YES];
575+
}
576+
575577
- (void)testStopAllFetchersSeparatelyWithCallbacks {
576578
[self stopAllFetchersHelperUseStopAllAPI:NO callbacksAfterStop:YES];
577579
}
@@ -599,9 +601,10 @@ - (void)stopAllFetchersHelperUseStopAllAPI:(BOOL)useStopAllAPI
599601
XCTestExpectation *fetcherCallbackExpectation =
600602
[[XCTNSNotificationExpectation alloc] initWithName:@"callback"];
601603
if (doStopCallbacks) {
602-
fetcherCallbackExpectation.expectedFulfillmentCount = 4;
604+
fetcherCallbackExpectation.expectedFulfillmentCount = urlArray.count;
603605
fetcherCallbackExpectation.assertForOverFulfill = YES;
604606
} else {
607+
// No callbacks, just complete this expectation now.
605608
[fetcherCallbackExpectation fulfill];
606609
}
607610

0 commit comments

Comments
 (0)