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

View File

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