-
-
Save gfontenot/d184197dbc65bb23d72f12abad386ce9 to your computer and use it in GitHub Desktop.
goroutines.swift
This file contains 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 Foundation | |
enum Message<T> { | |
case value(T) | |
case finished | |
} | |
protocol Channel: IteratorProtocol { | |
func send(_ value: Message<Element>) | |
} | |
/// A blocking channel for sending values. | |
/// | |
/// `send` and `receive` must run in separate separate execution contexts, otherwise you get a deadlock. | |
final class BlockingChannel<A>: Channel { | |
let producer = DispatchSemaphore(value: 0) | |
let consumer = DispatchSemaphore(value: 0) | |
private var value: Message<A> = .finished | |
func send(_ value: Message<A>) { | |
producer.wait() | |
self.value = value | |
consumer.signal() | |
} | |
func next() -> A? { | |
guard case let .value(v) = self.value else { return nil } | |
producer.signal() | |
consumer.wait() | |
return v | |
} | |
} | |
final class BufferedChannel<A>: Channel { | |
let valueQueue = DispatchQueue(label: "sync value") | |
let consumer = DispatchSemaphore(value: 0) | |
private var values: [A] = [] | |
var done: Bool = false | |
func send(_ value: Message<A>) { | |
switch value { | |
case let .value(v): | |
self.valueQueue.sync { | |
self.values.append(v) | |
} | |
case .finished: | |
self.done = true | |
} | |
consumer.signal() | |
} | |
func next() -> A? { | |
guard !done else { | |
return values.isEmpty ? nil : values.removeFirst() | |
} | |
consumer.wait() | |
var result: A? = nil | |
valueQueue.sync { | |
result = values.removeFirst() | |
} | |
return result | |
} | |
} | |
func go(_ f: @escaping () -> ()) -> () { | |
DispatchQueue.global().async { | |
f() | |
} | |
} | |
infix operator <- | |
func <-<C, A>(lhs: C, rhs: A) where C: Channel, C.Element == A { | |
lhs <- .value(rhs) | |
} | |
func <-<C, A>(lhs: C, rhs: Message<A>) where C: Channel, C.Element == A { | |
lhs.send(rhs) | |
} | |
func test() { | |
let channel = BufferedChannel<Int>() | |
go { | |
channel <- 1 | |
channel <- 2 | |
channel <- .finished // done | |
} | |
sleep(1) | |
while let value = channel.next() { | |
print(value) | |
} | |
} | |
test() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment