<commit_before>//
//  WebTask.swift
//  NetKit
//
//  Created by <NAME> on 2/12/16.
//  Copyright © 2016 <NAME>. All rights reserved.
//

import Foundation

public enum WebTaskResult {
  case success, failure(Error)
}

public enum WebTaskError: Error {
  case jsonSerializationFailedNilResponseBody
}

class Observer: NSObject {
  override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "operationCount" {
      if let queue = object as? OperationQueue, queue.operationCount == 0 {
        if let semaphore = context?.assumingMemoryBound(to: DispatchSemaphore.self).pointee {
          semaphore.signal()
        }
      }
    }
  }
}

public class WebTask {
  
  public enum TaskType {
    case data, download, upload
  }
  
  public typealias ResponseHandler = (Data?, URL?, URLResponse?) -> WebTaskResult
  public typealias JSONHandler = (Any) -> WebTaskResult
  public typealias FileDownloadHandler = (URL?, URLResponse?) -> WebTaskResult
  public typealias ErrorHandler = (Error) -> Void
  
  fileprivate let queueObserver = Observer()
  fileprivate let handlerQueue: OperationQueue = {
    let queue = OperationQueue()
    queue.maxConcurrentOperationCount = 1
    queue.isSuspended = true
    return queue
  }()
  
  fileprivate var webRequest: WebRequest
  fileprivate weak var webService: WebService?
  fileprivate let taskType: TaskType
  fileprivate var urlTask: URLSessionTask?
  
  fileprivate var urlResponse: URLResponse?
  fileprivate var responseData: Data?
  fileprivate var responseURL: URL?
  fileprivate var taskResult: WebTaskResult?
  
  fileprivate var semaphore: DispatchSemaphore?
  fileprivate var timeout: Int = -1
  
  fileprivate var authCount: Int = 0
  fileprivate var fileDownloadHandler: FileDownloadHandler?
  
//  private var useOriginQueue = false
//  private let originQueue: NSOperationQueue = {
//    return NSOperationQueue.currentQueue() ?? NSOperationQueue()
//  }()
  
  deinit {
//    handlerQueue.cancelAllOperations()
    handlerQueue.removeObserver(queueObserver, forKeyPath: "operationCount")
  }
  
  public init(webRequest: WebRequest, webService: WebService, taskType: TaskType = .data) {
    self.webRequest = webRequest
    self.webService = webService
    self.taskType = taskType
  }
}

extension WebTask {
  
  @discardableResult public func resume() -> Self {
    
    handlerQueue.addObserver(queueObserver, forKeyPath: "operationCount", options: .new, context: &semaphore)
    
    if urlTask == nil {
      switch taskType {
      case .data:
        urlTask = webService?.taskSource.nkDataTask?(with: webRequest.urlRequest) { data, response, error in
          self.handleResponse(data, response: response, error: error)
        }
      case .download:
        urlTask = webService?.taskSource.nkDownloadTask?(with: webRequest.urlRequest) { location, response, error in
          self.handleResponse(location: location, response: response, error: error)
        }
      case .upload:
        urlTask = webService?.taskSource.nkUploadTask?(with: webRequest.urlRequest, from: webRequest.body) { data, response, error in
          self.handleResponse(data, response: response, error: error)
        }
      }
    }
    
    if let task = urlTask {
      webService?.webDelegate?.tasks[task.taskIdentifier] = self
      task.resume()
    }
    
    if let semaphore = semaphore {
      let time = DispatchTime.now() + Double(Int64(UInt64(timeout) * NSEC_PER_SEC)) / Double(NSEC_PER_SEC)
      let result = semaphore.wait(timeout: time)
      if result == DispatchTimeoutResult.timedOut && urlTask?.state != .completed {
        cancel()
      }
      handlerQueue.waitUntilAllOperationsAreFinished()
    } else if timeout == 0 {
      handlerQueue.waitUntilAllOperationsAreFinished()
    }
    return self
  }
  
  @discardableResult public func resumeAndWait(_ timeout: Int = 0) -> Self {
    self.timeout = timeout
    if timeout > 0 {
      semaphore = DispatchSemaphore(value: 0)
    }
    return resume()
  }
  
  public func suspend() {
    urlTask?.suspend()
  }
  
  public func cancel() {<commit_msg>Fixed crash when semaphore is null.<commit_after>//
//  WebTask.swift
//  NetKit
//
//  Created by <NAME> on 2/12/16.
//  Copyright © 2016 <NAME>. All rights reserved.
//

import Foundation

public enum WebTaskResult {
  case success, failure(Error)
}

public enum WebTaskError: Error {
  case jsonSerializationFailedNilResponseBody
}

class Observer: NSObject {
  override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
    if keyPath == "operationCount" {
      if let queue = object as? OperationQueue, queue.operationCount == 0 {
        let semaphore = context?.assumingMemoryBound(to: DispatchSemaphore.self).pointee
        semaphore?.signal()
      }
    }
  }
}

public class WebTask {
  
  public enum TaskType {
    case data, download, upload
  }
  
  public typealias ResponseHandler = (Data?, URL?, URLResponse?) -> WebTaskResult
  public typealias JSONHandler = (Any) -> WebTaskResult
  public typealias FileDownloadHandler = (URL?, URLResponse?) -> WebTaskResult
  public typealias ErrorHandler = (Error) -> Void
  
  fileprivate let queueObserver = Observer()
  fileprivate let handlerQueue: OperationQueue = {
    let queue = OperationQueue()
    queue.maxConcurrentOperationCount = 1
    queue.isSuspended = true
    return queue
  }()
  
  fileprivate var webRequest: WebRequest
  fileprivate weak var webService: WebService?
  fileprivate let taskType: TaskType
  fileprivate var urlTask: URLSessionTask?
  
  fileprivate var urlResponse: URLResponse?
  fileprivate var responseData: Data?
  fileprivate var responseURL: URL?
  fileprivate var taskResult: WebTaskResult?
  
  fileprivate var semaphore: DispatchSemaphore?
  fileprivate var timeout: Double = -1
  
  fileprivate var authCount: Int = 0
  fileprivate var fileDownloadHandler: FileDownloadHandler?
  
//  private var useOriginQueue = false
//  private let originQueue: NSOperationQueue = {
//    return NSOperationQueue.currentQueue() ?? NSOperationQueue()
//  }()
  
  deinit {
//    handlerQueue.cancelAllOperations()
    handlerQueue.removeObserver(queueObserver, forKeyPath: "operationCount")
  }
  
  public init(webRequest: WebRequest, webService: WebService, taskType: TaskType = .data) {
    self.webRequest = webRequest
    self.webService = webService
    self.taskType = taskType
  }
}

extension WebTask {
  
  @discardableResult public func resume() -> Self {
    
    handlerQueue.addObserver(queueObserver, forKeyPath: "operationCount", options: .new, context: &semaphore)
    
    if urlTask == nil {
      switch taskType {
      case .data:
        urlTask = webService?.taskSource.nkDataTask?(with: webRequest.urlRequest) { data, response, error in
          self.handleResponse(data, response: response, error: error)
        }
      case .download:
        urlTask = webService?.taskSource.nkDownloadTask?(with: webRequest.urlRequest) { location, response, error in
          self.handleResponse(location: location, response: response, error: error)
        }
      case .upload:
        urlTask = webService?.taskSource.nkUploadTask?(with: webRequest.urlRequest, from: webRequest.body) { data, response, error in
          self.handleResponse(data, response: response, error: error)
        }
      }
    }
    
    if let task = urlTask {
      webService?.webDelegate?.tasks[task.taskIdentifier] = self
      task.resume()
    }
    
    if let semaphore = semaphore {
      let result = semaphore.wait(timeout: DispatchTime.now() + timeout)
      if result == DispatchTimeoutResult.timedOut && urlTask?.state != .completed {
        cancel()
      }
      handlerQueue.waitUntilAllOperationsAreFinished()
    } else if timeout == 0 {
      handlerQueue.waitUntilAllOperationsAreFinished()
    }
    return self
  }
  
  @discardableResult public func resumeAndWait(_ timeout: Double = 0) -> Self {
    self.timeout = timeout
    if timeout > 0 {
      semaphore = DispatchSemaphore(value: 0)
    }
    return resume()
  }
  
  public func suspend() {
    urlTask?.suspend()
  }
  
  public func cancel() {
{'fraction_non_alphanumeric': 0.09501388385850537, 'fraction_numerical': 0.004587709766992636, 'mean_word_length': 3.2989102231447847, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 12, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6408327', 'n_tokens_mistral': 2424, 'n_tokens_neox': 2393, 'n_words': 633}