hi! this is basically a follow-up question to this post from a couple years ago. i'm trying to understand what the intended behavior of AsyncStream is when being 'consumed' via multiple (concurrent) iterators. the evolution document for AsyncStream states:
As with any sequence, iterating over an AsyncStream multiple times, or creating multiple iterators and iterating over them separately, may produce an unexpected series of values. Neither AsyncStream nor its iterator are @Sendable types, and concurrent iteration is considered a programmer error.
additionally the docs for AsyncStream.Iterator state:
This type doesn’t conform to Sendable. Don’t use it from multiple concurrent contexts. It is a programmer error to invoke next() from a concurrent context that contends with another such call, which results in a call to fatalError().
however, one can seemingly iterate over an AsyncStream from multiple Tasks, and there is neither compile-time nor runtime feedback that this may violate the API (at least that i could see). for example:
func multiConsume() async {
var startStream: (() -> Void)!
let stream = AsyncStream<Int> { continuation in
startStream = {
Task {
for i in 0..<1_000 {
try! await Task.sleep(nanoseconds: 1_000_000)
continuation.yield(i)
}
continuation.finish()
}
}
}
let t1 = Task {
// this races with starting the stream but still illustrates the scenario in question
for await value in stream {
print ("task 1: \(value)")
}
}
let t2 = Task {
for await value in stream {
print ("task 2: \(value)")
}
}
startStream()
_ = await (t1.value, t2.value)
}
this example will consume the elements of the stream, but which one is consumed by which for await loop is arbitray. however, if we swap out the AsyncStream with an AsyncThrowingStream, then the code will generally crash at runtime with the alluded-to fatal error 'attempt to await next() on more than one task'.
is there a reason for this behavioral difference between the two flavors of AsyncStream, or should they both have runtime checks to attempt to prevent concurrent iteration?