Skip to content

Commit

Permalink
Merge pull request onevcat#1112 from onevcat/fix/prefetch-queue
Browse files Browse the repository at this point in the history
Use the same queue for all prefetchers
  • Loading branch information
onevcat authored Feb 10, 2019
2 parents 9d00dc9 + e23a2ea commit f0921c7
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 47 deletions.
78 changes: 31 additions & 47 deletions Sources/Networking/ImagePrefetcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,7 @@ public class ImagePrefetcher {

/// The maximum concurrent downloads to use when prefetching images. Default is 5.
public var maxConcurrentDownloads = 5

// The dispatch queue to use for handling resource process, so downloading does not occur on the main thread
// This prevents stuttering when preloading images in a collection view or table view.
private var prefetchQueue: DispatchQueue


private let prefetchResources: [Resource]
private let optionsInfo: KingfisherParsedOptionsInfo
Expand Down Expand Up @@ -138,10 +135,6 @@ public class ImagePrefetcher {
prefetchResources = resources
pendingResources = ArraySlice(resources)

// Set up the dispatch queue that all our work should occur on.
let prefetchQueueName = "com.onevcat.Kingfisher.PrefetchQueue"
prefetchQueue = DispatchQueue(label: prefetchQueueName)

// We want all callbacks from our prefetch queue, so we should ignore the callback queue in options.
// Add our own callback dispatch queue to make sure all internal callbacks are
// coming back in our expected queue.
Expand All @@ -159,45 +152,38 @@ public class ImagePrefetcher {
/// Starts to download the resources and cache them. This can be useful for background downloading
/// of assets that are required for later use in an app. This code will not try and update any UI
/// with the results of the process.
public func start()
{
// Since we want to handle the resources cancellation in the prefetch queue only.
prefetchQueue.async {

guard !self.stopped else {
assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.")
self.handleComplete()
return
}

guard self.maxConcurrentDownloads > 0 else {
assertionFailure("There should be concurrent downloads value should be at least 1.")
self.handleComplete()
return
}
public func start() {
guard !stopped else {
assertionFailure("You can not restart the same prefetcher. Try to create a new prefetcher.")
handleComplete()
return
}

// Empty case.
guard self.prefetchResources.count > 0 else {
self.handleComplete()
return
}

let initialConcurrentDownloads = min(self.prefetchResources.count, self.maxConcurrentDownloads)
for _ in 0 ..< initialConcurrentDownloads {
if let resource = self.pendingResources.popFirst() {
self.startPrefetching(resource)
}
guard maxConcurrentDownloads > 0 else {
assertionFailure("There should be concurrent downloads value should be at least 1.")
handleComplete()
return
}

// Empty case.
guard prefetchResources.count > 0 else {
handleComplete()
return
}

let initialConcurrentDownloads = min(prefetchResources.count, maxConcurrentDownloads)
for _ in 0 ..< initialConcurrentDownloads {
if let resource = self.pendingResources.popFirst() {
self.startPrefetching(resource)
}
}
}

/// Stops current downloading progress, and cancel any future prefetching activity that might be occuring.
public func stop() {
prefetchQueue.async {
if self.finished { return }
self.stopped = true
self.tasks.values.forEach { $0.cancel() }
}
if finished { return }
stopped = true
tasks.values.forEach { $0.cancel() }
}

func downloadAndCache(_ resource: Resource) {
Expand Down Expand Up @@ -273,13 +259,11 @@ public class ImagePrefetcher {
}

func reportCompletionOrStartNext() {
prefetchQueue.async {
if let resource = self.pendingResources.popFirst() {
self.startPrefetching(resource)
} else {
guard self.tasks.isEmpty else { return }
self.handleComplete()
}
if let resource = self.pendingResources.popFirst() {
startPrefetching(resource)
} else {
guard tasks.isEmpty else { return }
handleComplete()
}
}

Expand Down
16 changes: 16 additions & 0 deletions Tests/KingfisherTests/ImagePrefetcherTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -298,4 +298,20 @@ class ImagePrefetcherTests: XCTestCase {

waitForExpectations(timeout: 3, handler: nil)
}

func testPrefetchMultiTimes() {
let exp = expectation(description: #function)
let group = DispatchGroup()

testURLs.forEach { stub($0, data: testImageData) }
for _ in 0..<10000 {
group.enter()
let prefetcher = ImagePrefetcher(resources: testURLs) { _, _, _ in
group.leave()
}
prefetcher.start()
}
group.notify(queue: .main) { exp.fulfill() }
waitForExpectations(timeout: 3, handler: nil)
}
}

0 comments on commit f0921c7

Please sign in to comment.