     
 }
 
+// MARK: - DefaultTaskTracker
+final public class DefaultTaskTracker: TaskTracker {
+    public private(set) var progressInfo: ProgressInfo?
+    
+    private var lock = OS_SPINLOCK_INIT
+    
+    private var trackings: [TrackingToken: (progress: DownloadProgressHandler?, completion: DownloadCompletionHandler?)] = [:]
+    
+    public func addTracking(progress progress: DownloadProgressHandler?, completion: DownloadCompletionHandler?) -> TrackingToken? {
+        guard progress != nil && completion != nil else { return nil }
+        let token = TrackingToken()
+        spinLock(&lock) { trackings[token] = (progress, completion) }
+        return token
+    }
+    
+    public func removeTracking(token: TrackingToken?) {
+        if let token = token {
+            spinLock(&lock) { trackings.removeValueForKey(token) }
+        }
+    }
+    
+    private func notifyProgress(progress: ProgressInfo) {
+        var progresses: [DownloadProgressHandler]!
+        spinLock(&lock) { progresses = self.trackings.flatMap{$1.progress} }
+        progresses.forEach { $0(progress) }
+    }
+    
+    private func notifyCompletion(result: Result) {
+        var completions: [DownloadCompletionHandler]!
+        spinLock(&lock) { completions = self.trackings.flatMap{$1.completion} }
+        completions.forEach { $0(result) }
+    }
+}
+
+// MARK: -
 
 final private class URLSessionDelegate: NSObject, NSURLSessionDownloadDelegate {
     private var queue: dispatch_queue_t

    
    public func download(url: NSURL, taskGenerator: TaskGenerator?, cache: Cache?, progress: DownloadProgressHandler?, completion: DownloadCompletionHandler) -> Task {
        var task: Task!
        let taskGenerator: TaskGenerator! = (taskGenerator != nil ? taskGenerator : self.taskGenerator)
        dispatch_sync(queue) { [queue, operationQueue] in
            if let data = cache?.cacheFromMemory(url) {
                task = InMemoryTask()
                dispatch_async(queue) {
                    if task.cancelled {
                        notifyProgressInvalid(progress)
                        completion(.Cancelled(url: url))
                    } else {
                        let length = Int64(data.length)
                        progress?((length, length ,length))
                        completion(.Success(url: url, data: data))
                    }
                }
            } else {
                let combinedTask = CombinedDownloadTask()

                    if let diskURL = cache?.diskURLForCachedURL(url), data = NSData(contentsOfURL: diskURL) {
                        dispatch_sync(queue) {
                            if combinedTask.cancelled {
                                tracker.notifyProgress(PTInvalidProgressInfo)
                                tracker.notifyCompletion(.Cancelled(url: url))
                            } else {
                                let length = Int64(data.length)
                                tracker.notifyProgress((length, length ,length))
                                tracker.notifyCompletion(.Success(url: url, data: data))
                            }
                            self?.removeTracker(task)
                        }
                    } else {
                        dispatch_sync(queue) {
                            if !combinedTask.cancelled {
                                combinedTask.sessionTask = taskGenerator(url: url,
                                    progress: { c, tr, te in
                                        tracker.notifyProgress((c, tr, te))
                                    },
                                    completion: { result in
                                        let _ = combinedTask // just keep task's live
                                        if case .Success(let url, let data) = result {
                                            cache?.cache(url, data: data)
                                        } else {
                                            tracker.notifyProgress(PTInvalidProgressInfo)
                                        }
                                        tracker.notifyCompletion(result)
                                        self?.removeTracker(task)
                                })
                            } else {
                                tracker.notifyProgress(PTInvalidProgressInfo)
                                tracker.notifyCompletion(.Cancelled(url: url))
                                self?.removeTracker(task)
                            }
                        }
                    }
