swift-async-algorithms icon indicating copy to clipboard operation
swift-async-algorithms copied to clipboard

Should channel operations automatically check for cancelation?

Open paulofaria opened this issue 3 years ago • 1 comments

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)
    }
}

paulofaria avatar Jun 16 '22 13:06 paulofaria

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.

phausler avatar Jun 16 '22 16:06 phausler

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 to finish() 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

FranzBusch avatar Jun 15 '23 09:06 FranzBusch