Skip to content

Commit

Permalink
Add semaphore
Browse files Browse the repository at this point in the history
  • Loading branch information
onevcat committed Jun 2, 2017
1 parent c676d71 commit 1035327
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 13 deletions.
47 changes: 35 additions & 12 deletions Sources/ImageDownloader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ open class ImageDownloader {

var downloadTaskCount = 0
var downloadTask: RetrieveImageDownloadTask?
var cancelSemaphore: DispatchSemaphore?
}

// MARK: - Public property
Expand Down Expand Up @@ -219,6 +220,7 @@ open class ImageDownloader {
// MARK: - Internal property
let barrierQueue: DispatchQueue
let processQueue: DispatchQueue
let cancelQueue: DispatchQueue

typealias CallbackPair = (progressBlock: ImageDownloaderProgressBlock?, completionHandler: ImageDownloaderCompletionHandler?)

Expand All @@ -242,6 +244,7 @@ open class ImageDownloader {

barrierQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Barrier.\(name)", attributes: .concurrent)
processQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Process.\(name)", attributes: .concurrent)
cancelQueue = DispatchQueue(label: "com.onevcat.Kingfisher.ImageDownloader.Cancel.\(name)")

sessionHandler = ImageDownloaderSessionHandler()

Expand Down Expand Up @@ -312,7 +315,7 @@ open class ImageDownloader {

dataTask.priority = options?.downloadPriority ?? URLSessionTask.defaultPriority
dataTask.resume()
delegate?.imageDownloader(self, willDownloadImageForURL: url, with: request)
self.delegate?.imageDownloader(self, willDownloadImageForURL: url, with: request)

// Hold self while the task is executing.
self.sessionHandler.downloadHolder = self
Expand All @@ -332,19 +335,34 @@ open class ImageDownloader {
extension ImageDownloader {

// A single key may have multiple callbacks. Only download once.
func setup(progressBlock: ImageDownloaderProgressBlock?, with completionHandler: ImageDownloaderCompletionHandler?, for url: URL, options: KingfisherOptionsInfo?, started: ((URLSession, ImageFetchLoad) -> Void)) {
func setup(progressBlock: ImageDownloaderProgressBlock?, with completionHandler: ImageDownloaderCompletionHandler?, for url: URL, options: KingfisherOptionsInfo?, started: @escaping ((URLSession, ImageFetchLoad) -> Void)) {

barrierQueue.sync(flags: .barrier) {
let loadObjectForURL = fetchLoads[url] ?? ImageFetchLoad()
let callbackPair = (progressBlock: progressBlock, completionHandler: completionHandler)

loadObjectForURL.contents.append((callbackPair, options ?? KingfisherEmptyOptionsInfo))

fetchLoads[url] = loadObjectForURL

if let session = session {
started(session, loadObjectForURL)
func prepareFetchLoad() {
barrierQueue.sync(flags: .barrier) {
let loadObjectForURL = fetchLoads[url] ?? ImageFetchLoad()
let callbackPair = (progressBlock: progressBlock, completionHandler: completionHandler)

loadObjectForURL.contents.append((callbackPair, options ?? KingfisherEmptyOptionsInfo))

fetchLoads[url] = loadObjectForURL

if let session = session {
started(session, loadObjectForURL)
}
}
}

if let fetchLoad = fetchLoad(for: url), fetchLoad.downloadTaskCount == 0 {
if fetchLoad.cancelSemaphore == nil {
fetchLoad.cancelSemaphore = DispatchSemaphore(value: 0)
}
cancelQueue.async {
_ = fetchLoad.cancelSemaphore?.wait(timeout: .distantFuture)
fetchLoad.cancelSemaphore = nil
prepareFetchLoad()
}
} else {
prepareFetchLoad()
}
}

Expand Down Expand Up @@ -464,6 +482,11 @@ class ImageDownloaderSessionHandler: NSObject, URLSessionDataDelegate, Authentic
// We need to clean the fetch load first, before actually calling completion handler.
cleanFetchLoad(for: url)

var leftSignal: Int
repeat {
leftSignal = fetchLoad.cancelSemaphore?.signal() ?? 0
} while leftSignal != 0

for content in fetchLoad.contents {
content.options.callbackDispatchQueue.safeAsync {
content.callback.completionHandler?(nil, error as NSError, url, nil)
Expand Down
2 changes: 1 addition & 1 deletion Tests/KingfisherTests/ImageDownloaderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ class ImageDownloaderTests: XCTestCase {
}

// Issue 532 https://github.com/onevcat/Kingfisher/issues/532#issuecomment-305644311
func _testCancelThenRestartSameDownload() {
func testCancelThenRestartSameDownload() {
let expectation = self.expectation(description: "wait for downloading")

let URLString = testKeys[0]
Expand Down

0 comments on commit 1035327

Please sign in to comment.