[swift-evolution] [Concurrency] Fixing race conditions in async/await example

Maxim Veksler maxim at vekslers.org
Wed Aug 23 23:26:08 CDT 2017


I think that the solution you are describing is how RxSwift (ReactiveX)
solves this problem.

I believe Rx, like many other higher level abstractions would benefit from
async, actors behind the scenes, as an implementation detail.

‫בתאריך יום ד׳, 23 באוג׳ 2017 ב-20:41 מאת ‪Joe Groff via swift-evolution‬‏
<‪swift-evolution at swift.org‬‏>:‬

>
> On Aug 19, 2017, at 4:56 AM, Jakob Egger via swift-evolution <
> swift-evolution at swift.org> wrote:
>
> I've read async/await proposal, and I'm thrilled by the possibilities.
> Here's what I consider the canonical example:
>
> @IBAction func buttonDidClick(sender:AnyObject) {
>   beginAsync {
>     let image = await processImage()
>     imageView.image = image
>   }
> }
>
> This is exactly the kind of thing I will use async/await for!
>
> But while this example looks extremely elegant, it would suffer from a
> number of problems in practice:
>
> 1. There is no guarantee that you are on the main thread after `await
> processImage()`
> 2. There is no way to cancel processing
> 3. Race Condition: If you click the button a second time before
> `processImage()` is done, two copies will run simultaneously and you don't
> know which image will "win".
>
> So I wondered: What would a more thorough example look like in practice?
> How would I fix all these issues?
>
> After some consideration, I came up with the following minimal example
> that addresses all these issues:
>
> class ImageProcessingTask {
>   var cancelled = false
>   func process() async -> Image? { … }
> }
>
> var currentTask: ImageProcessingTask?
> @IBAction func buttonDidClick(sender:AnyObject) {
>   currentTask?.cancelled = true
>   let task = ImageProcessingTask()
>   currentTask = task
>   beginAsync {
>     guard let image = await task.process() else { return }
>     DispatchQueue.main.async {
>       guard task.cancelled == false else { return }
>       imageView.image = image
>     }
>   }
> }
>
> If my example isn't obvious, I've documented my thinking (and some
> alternatives) in a gist:
> https://gist.github.com/jakob/22c9725caac5125c1273ece93cc2e1e7
>
> Anyway, this more realistic code sample doesn't look nearly as nice any
> more, and I actually think this could be implemented nicer without
> async/await:
>
> class ImageProcessingTask {
>   var cancelled = false
>   func process(completionQueue: DispatchQueue, completionHandler:
> (Image?)->()) { … }
> }
> @IBAction func buttonDidClick(sender:AnyObject) {
>   currentTask?.cancelled = true
>   let task = ImageProcessingTask()
>   currentTask = task
>   task.process(completionQueue: DispatchQueue.main) { (image) in
>     guard let image = image else { return }
> guard task.cancelled == false else { return }
> imageView.image = image
>   }
> }
>
> So I wonder: What's the point of async/await if it doesn't result in nicer
> code in practice? How can we make async/await more elegant when calling
> from non-async functions?
>
>
> Yeah, it's important to understand that coroutines don't directly offer
> any form of coordination; they only let you thread execution nicely through
> existing coordination mechanisms. IBActions by themselves don't offer any
> coordination, so anything more than fire-and-forget is still going to
> require explicit code. There are some interesting approaches you still
> might be able to explore to make this kind of thing nicer; for instance, if
> buttonDidClick didn't directly trigger the task, but instead communicated
> with a coroutine via synchronous channels in the style of Go, then that
> coroutine could be responsible for filtering multiple click events, and
> could also listen for cancellation events. The actor model Chris proposes
> in his document could conceivably let you wrap up that low-level channel
> management in a nice OO-looking wrapper.
>
> -Joe
>
> _______________________________________________
> 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/20170824/3d61f320/attachment.html>


More information about the swift-evolution mailing list