-
Notifications
You must be signed in to change notification settings - Fork 152
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add MultiProducerSingleConsumerChannel
#305
base: main
Are you sure you want to change the base?
Conversation
# Motivation The pitch to add external backpressure support to the standard libraries `AsyncStream` got returned for revision since there are larger open questions around `AsyncSequence`. However, having external backpressure in a source asynchronous sequence is becoming more and more important. # Modification This PR adds a modified proposal and implementation that brings the Swift Evolution proposal over to Swift Async Algorithms.
ddd7523
to
65e8957
Compare
@swift-ci please test |
4904039
to
662693e
Compare
AsyncBackpressuredStream
proposal and implementationMultiProducerSingleConsumerChannel
|
||
### Upstream producer termination | ||
|
||
The producer will get notified about termination through the `onTerminate` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can you add calls to onTerminate
to the snippets below?
}) | ||
} | ||
} catch { | ||
// `send(contentsOf:)` throws if the asynchronous stream already terminated |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this be clearer as another case on the sendResult?
/// - Parameters: | ||
/// - low: When the number of buffered elements drops below the low watermark, producers will be resumed. | ||
/// - high: When the number of buffered elements rises above the high watermark, producers will be suspended. | ||
/// - waterLevelForElement: A closure used to compute the contribution of each buffered element to the current water level. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, neat. This works nicely for collections.
for producerContinuation in producerContinuations { | ||
switch producerContinuation { | ||
case .closure(let onProduceMore): | ||
onProduceMore(.failure(CancellationError())) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a state machine bug I picked up running the OpenAPIURLSession tests with this type:
onProduceMore(.failure(CancellationError())) | |
onProduceMore(.success(())) |
Might be worth seeing if we can have similar tests in this project. This particular test, 1kChunk_10kChunks_100BDownloadWatermark
, exercised the suspend and resume of the producer a lot due to the relation of the chunk size to the watermark.
public static func makeChannel( | ||
of elementType: Element.Type = Element.self, | ||
throwing failureType: Failure.Type = Never.self, | ||
backpressureStrategy: Source.BackpressureStrategy | ||
) -> ChannelAndStream { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason for this returning a nominal type? Previously it returned a tuple which allows for initialization of the sequence as follows:
let (channel, source) = MultiProducerSingleConsumerChannel.makeChannel(/* ... */)
This is also what the documentation comment on the type suggests.
However, with the nominal type you cannot do that and, if you are storing these as properties, are forced to do this...
let channelAndSource = MultiProducerSingleConsumerChannel.makeChannel(/* ... */)
self.channel = channelAndSource.channel
self.source = channelAndSource.source
...instead of this...
(self.channel, self.source) = MultiProducerSingleConsumerChannel.makeChannel(/* ... */)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sadly, tuples don't support ~Copyable
types. That's the only reason for a nominal type here.
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#if compiler(>=6.0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please can we consider offering a pre-Swift 6 variant of this type. There are several places we'd like to adopt this that need to also support Swift 5.9+ for the foreseeable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm not sure yet. Especially since it is part of the public API in the Source
. Making the Source
copyable makes the type behave way differently.
private let backing: _Backing | ||
|
||
@frozen | ||
public struct ChannelAndStream: ~Copyable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was it intended to be called ChannelAndStream
while it is actually a container for channel
and source
?
Shouldn't it be called ChannelAndSource
instead?
Hi!
|
@ser-0xff Thanks for trying it out. We don't plan on support library evolution mode in any of our packages. What we normally advise is to not compile packages as frameworks but rather add this library as an |
Thank you for the reply... |
@ser-0xff I guess we need to wrap up the API surface we want to support then and just do internal import as suggested? But let's take that offline from this PR - thanks @FranzBusch for quick reply. |
Hi, @FranzBusch Here is a minimized test showing the usage pattern we want if you will find some time to look at. |
The source has a |
Missed |
HI, Franz! |
You are right. I haven't gotten around to implement the correct handling of multiple sources yet. |
Motivation
The pitch to add external backpressure support to the standard libraries
AsyncStream
got returned for revision since there are larger open questions aroundAsyncSequence
. However, having external backpressure in a source asynchronous sequence is becoming more and more important.Modification
This PR adds a modified proposal and implementation that brings the Swift Evolution proposal over to Swift Async Algorithms.