Skip to content

Instantly share code, notes, and snippets.

@chriseidhof
Created February 16, 2018 12:36
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save chriseidhof/d70244716ce0a40661b9e71269791b03 to your computer and use it in GitHub Desktop.
Save chriseidhof/d70244716ce0a40661b9e71269791b03 to your computer and use it in GitHub Desktop.
goroutines.swift
import Foundation
protocol Channel: IteratorProtocol {
func send(_ value: 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: A?
var done: Bool = false
func send(_ value: A?) {
producer.wait()
self.value = value
if value == nil { done = true }
consumer.signal()
}
func next() -> A? {
guard !done else { return nil }
producer.signal()
consumer.wait()
return value
}
}
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: A?) {
if let v = value {
self.valueQueue.sync {
self.values.append(v)
}
} else {
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.send(rhs)
}
func test() {
let channel = BufferedChannel<Int>()
go {
channel <- 1
channel <- 2
channel <- nil // done
}
sleep(1)
while let value = channel.next() {
print(value)
}
}
test()
@moreindirection
Copy link

Nice implementation!

Should the code inside BufferedChannel#send be inside a "valuesQueue.sync"? What if one thread sends a new value after another thread sends a nil to finish the channel? Or what if two threads simultaneously invoke next() after the channel is done?

@13hoop
Copy link

13hoop commented Feb 18, 2018

nice
thanks for sharing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment