Fix crashing bug in FetchRequestQueue by making sure the completion is called not more than once from FetchRequestOperation. Fix #1133.

This commit is contained in:
Brent Simmons 2019-10-13 22:08:05 -07:00
parent fcb0f0c439
commit 31a2afb067
2 changed files with 20 additions and 13 deletions

View File

@ -16,7 +16,7 @@ import Articles
typealias FetchRequestOperationResultBlock = (Set<Article>, FetchRequestOperation) -> Void typealias FetchRequestOperationResultBlock = (Set<Article>, FetchRequestOperation) -> Void
class FetchRequestOperation { final class FetchRequestOperation {
let id: Int let id: Int
let resultBlock: FetchRequestOperationResultBlock let resultBlock: FetchRequestOperationResultBlock
@ -35,8 +35,17 @@ class FetchRequestOperation {
precondition(Thread.isMainThread) precondition(Thread.isMainThread)
precondition(!isFinished) precondition(!isFinished)
var didCallCompletion = false
func callCompletionIfNeeded() {
if !didCallCompletion {
didCallCompletion = true
completion(self)
}
}
if isCanceled { if isCanceled {
completion(self) callCompletionIfNeeded()
return return
} }
@ -44,7 +53,7 @@ class FetchRequestOperation {
if articleFetchers.isEmpty { if articleFetchers.isEmpty {
isFinished = true isFinished = true
resultBlock(Set<Article>(), self) resultBlock(Set<Article>(), self)
completion(self) callCompletionIfNeeded()
return return
} }
@ -52,22 +61,21 @@ class FetchRequestOperation {
var fetchersReturned = 0 var fetchersReturned = 0
var fetchedArticles = Set<Article>() var fetchedArticles = Set<Article>()
for articleFetcher in articleFetchers { for articleFetcher in articleFetchers {
var didCallCompletion = false
articleFetcher.fetchArticlesAsync { (articles) in articleFetcher.fetchArticlesAsync { (articles) in
precondition(Thread.isMainThread) precondition(Thread.isMainThread)
if self.isCanceled { guard !self.isCanceled else {
if !didCallCompletion { callCompletionIfNeeded()
didCallCompletion = true
completion(self)
}
return return
} }
assert(!self.isFinished)
fetchedArticles.formUnion(articles) fetchedArticles.formUnion(articles)
fetchersReturned += 1 fetchersReturned += 1
if fetchersReturned == numberOfFetchers { if fetchersReturned == numberOfFetchers {
self.isFinished = true self.isFinished = true
self.resultBlock(fetchedArticles, self) self.resultBlock(fetchedArticles, self)
completion(self) callCompletionIfNeeded()
} }
} }
} }

View File

@ -10,7 +10,7 @@ import Foundation
// Main thread only. // Main thread only.
class FetchRequestQueue { final class FetchRequestQueue {
private var pendingRequests = [FetchRequestOperation]() private var pendingRequests = [FetchRequestOperation]()
private var currentRequest: FetchRequestOperation? = nil private var currentRequest: FetchRequestOperation? = nil
@ -40,9 +40,8 @@ private extension FetchRequestQueue {
currentRequest = requestToRun currentRequest = requestToRun
pendingRequests.removeFirst() pendingRequests.removeFirst()
requestToRun.run { (fetchRequestOperation) in currentRequest.run { (fetchRequestOperation) in
precondition(fetchRequestOperation === self.currentRequest) precondition(fetchRequestOperation === self.currentRequest)
precondition(fetchRequestOperation === requestToRun)
self.currentRequest = nil self.currentRequest = nil
self.runNextRequestIfNeeded() self.runNextRequestIfNeeded()
} }