[swift-evolution] [Concurrency] modifying beginAsync, suspendAsync to support cancellation

Howard Lovatt howard.lovatt at gmail.com
Mon Aug 28 20:42:21 CDT 2017


I think this makes the proposed async/await coroutines more viable.
Building on this, if the proposed Cancelable became:

    protocol ExecutionControl {
        /// Causes the executing coroutine to throw
`TerminateCoroutine.cancelled` and also terminates all the sub-coroutines.
        var isCancelled: Bool { set }

        /// If an await exceeds timeout then executing task throws
`TerminateCoroutine.timeout` and also terminates all the sub-coroutines.
        var timeout: DispatchTimeInterval { get set }
    }

Then async/await coroutine would have feature parity with a typical
`Future` - which would be good.

PS Effectively the execution service is returning the `Future`!

  -- Howard.

On 29 August 2017 at 09:42, Marc Schlichte via swift-evolution <
swift-evolution at swift.org> wrote:

>
> Am 19.08.2017 um 20:33 schrieb Marc Schlichte via swift-evolution <
> swift-evolution at swift.org>:
>
> Hi,
>
> to support cancellation, I propose the following changes to `beginAsync()`
> and `suspendAsync()`:
>
> `beginAsync()` returns an object adhering to a `Cancelable` protocol:
>
> ```
> func beginAsync(_ body: () async throws-> Void) rethrows -> Cancelable
>
> protocol Cancelable { func cancel() }
> ```
>
> `suspendAsync()` takes a new thunk parameter:
>
> ```
> func suspendAsync<T>(onCancel: () -> Void, body: (cont: (T) -> Void, err:
> (Error) -> Void) async -> T
> ```
>
> Now, when `cancel()` is invoked, the `onCancel` thunk in the current
> suspension (if any) will be called.
>
>
>
> Example:
>
> ```
> var task: Cancelable?
>
> @IBAction func buttonDidClick(sender: AnyObject) {
>  task = beginAsync {
>    do {
>      let image = try await processImage()
>      imageView.image = image
>    } catch AsyncError.canceled {
>      imageView.image = nil // or some fallback image...
>    } catch {
>      // other handling
>    }
>  }
> )
>
> @IBAction func cancelDidClick(sender: AnyObject) {
>  task?.cancel()
> }
>
>
> Just adding here that instead of directly using the low-level
> `beginAsync`, a Future/Promise could be used instead:
>
> ```
> var task: Future<UIImage>?
>
> @IBAction func buttonDidClick(sender: AnyObject) {
>   task = Future {
>     try await processImage()
>   }
>   do {
>     imageView.image = try await task!.get()
>   } catch AsyncError.canceled {
>     imageView.image = nil // or some fallback image...
>   } catch {
>     // other handling
>   }
> }
>
> @iBAction func cancelDidClick(sender: AnyObject) {
>   task?.cancel()
> }
> ```
>
> Of course, the init of Future would have to be changed
>
>   convenience init(_ body: () throws async -> T) {
>     self.init()
>     task = beginAsync {
>       do {
>         self.fulfill(try await body())
>       } catch {
>         self.fail(error)
>       }
>     }
>   }
>
> (BTW also added missing throws and try in code above)
>
> and `cancel()` would have to be added to `Future`:
>
> ```
> public func cancel() {
>   task?.cancel()
> }
>
> ```
>
>
> func processImage() async throws -> UIImage {
>  // This processing should be on a background queue (or better an Actor
> :-) - but ignored for this example
>  var cancelled = false
>  suspendAsync(onCancel: {
>
>    cancelled = true
>  }, body: { cont, err in
>     while !done && !cancelled {
>       // do the processing on image until done or canceled
>     }
>     guard !cancelled else { err(AsyncError.canceled) } // BTW, maybe
> change signature of `suspendAsync` to allow to throw here instead
>     cont(image)
>  }
> }
> ```
>
>
> ^ BTW, this should be `return  await suspendAsync(…`
>
>
> Cheers
> Marc
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution at swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170829/3df72fdd/attachment.html>


More information about the swift-evolution mailing list