swift-async-algorithms
swift-async-algorithms copied to clipboard
Should channel operations automatically check for cancelation?
libdill's implementation of structured concurrency fails on channel operations when a coroutine (analogous to a Task in Swift) is canceled. I think it might be good practice to do the same with Swift's structured concurrency. This might be true not just for channels, but any other operation where it makes sense in the family of async libraries? In fact, that's what libdill does. It even fails on channel creation. I know there are concerns about "The proliferation of try and now await", but if we really want to work within cooperative scheduling, we need to be sure developers are actually "cooperating". I'm guilty of this myself, most of the times I simply forget to use try Task.checkCancelation. Maybe this is a broader discussion that could be moved to the Swift forums. Let me know what you think.
If not the first-party API, we should at least have overloads that do that for us (I'm not too happy about this approach, though).
This:
public final class AsyncChannel {
...
func send(_ element: Element) async throws {
try Task.checkCancellation()
await send(element)
}
...
}
or this:
extension AsyncChannel {
/// This overload checks for cancelation...
@_disfavoredOverload
func send(_ element: Element) async throws {
try Task.checkCancellation()
await send(element)
}
}
The cancellation mechanism is handled on the other side of the channel, not the emitter side. If the consumer cancels, then the send will resume immediately.
Is the aim here to stop a pending send mid way through when the emitter's task is cancelled? If so we would kinda need to utilize a cancellation handler and immediately resume that pending continuation.
We clarified the behaviour of send in the recent doc update:
/// Sends an element to an awaiting iteration. This function will resume when the next call to
next()is made /// or when a call tofinish()is made from another task. /// If the channel is already finished then this returns immediately. /// If the task is cancelled, this function will resume without sending the element. /// Other sending operations from other tasks will remain active.
I think this solves the problem statement in the issue here. @paulofaria can you close the issue if that's what you expected please