Last active
          October 16, 2019 21:57 
        
      - 
      
- 
        Save hassanvfx/f19b229b607258876b6e2a4eadb5960a to your computer and use it in GitHub Desktop. 
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
  | import Dispatch | |
| private let coroutineQueue = DispatchQueue(label: "com.swift5.yield", attributes: .concurrent) | |
| public class Coroutine<Element>: IteratorProtocol { | |
| private let callerReady = DispatchSemaphore(value: 0) | |
| private let coroutineReady = DispatchSemaphore(value: 0) | |
| private var done: Bool = false | |
| private var transportStorage: Element? | |
| public typealias Yield = (Element) -> () | |
| public init(implementation: @escaping (Yield) -> ()) { | |
| coroutineQueue.async { | |
| // Don't start coroutine until first call. | |
| guard self.callerReady.wait(timeout: .distantFuture) == .success else { fatalError() } | |
| implementation { next in | |
| // Place element in transport storage, and let caller know it's ready. | |
| self.transportStorage = next | |
| self.coroutineReady.signal() | |
| // Don't continue coroutine until next call. | |
| guard self.callerReady.wait(timeout: .distantFuture) == .success else { fatalError() } | |
| } | |
| // The coroutine is forever over, so let's let the caller know. | |
| self.done = true | |
| self.coroutineReady.signal() | |
| } | |
| } | |
| public func next() -> Element? { | |
| // Make sure work is happening before we wait. | |
| guard !done else { return nil } | |
| // Return to the coroutine. | |
| callerReady.signal() | |
| // Wait until it has finished. | |
| guard coroutineReady.wait(timeout: .distantFuture) == .success else { fatalError() } | |
| defer { transportStorage = nil } | |
| return transportStorage | |
| } | |
| } | |
| /// | |
| private enum TransportStorage<Argument, Element> { | |
| case Input(Argument) | |
| case Output(Element) | |
| } | |
| public class ArgumentPassingCoroutine<Argument, Element> { | |
| private let callerReady = DispatchSemaphore(value: 0) | |
| private let coroutineReady = DispatchSemaphore(value: 0) | |
| private var done: Bool = false | |
| private var transportStorage: TransportStorage<Argument, Element>? | |
| public typealias Yield = (Element) -> Argument | |
| public init(implementation:@escaping (Yield) -> ()) { | |
| coroutineQueue.async() { | |
| // Don't start coroutine until first call. | |
| guard self.callerReady.wait(timeout: .distantFuture) == .success else { fatalError() } | |
| implementation { next in | |
| // Place element in transport storage, and let caller know it's ready. | |
| self.transportStorage = .Output(next) | |
| self.coroutineReady.signal() | |
| // Don't continue coroutine until next call. | |
| guard self.callerReady.wait(timeout: .distantFuture) == .success else { fatalError() } | |
| // Caller sent the next argument, so let's continue. | |
| defer { self.transportStorage = nil } | |
| guard case let .some(.Input(input)) = self.transportStorage else { fatalError() } | |
| return input | |
| } | |
| // The coroutine is forever over, so let's let the caller know. | |
| self.done = true | |
| self.coroutineReady.signal() | |
| } | |
| } | |
| public func next(_ argument: Argument) -> Element? { | |
| // Make sure work is happening before we wait. | |
| guard !done else { return nil } | |
| // Return to the coroutine, passing the argument. | |
| transportStorage = .Input(argument) | |
| callerReady.signal() | |
| // Wait until it has finished. | |
| guard coroutineReady.wait(timeout: .distantFuture) == .success else { fatalError() } | |
| // Return to the caller the result, then clear it. | |
| defer { transportStorage = nil } | |
| guard case let .some(.Output(output)) = transportStorage else { return nil } | |
| return output | |
| } | |
| } | |
  
    Sign up for free
    to join this conversation on GitHub.
    Already have an account?
    Sign in to comment