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

Adam Kemp adam.kemp at apple.com
Sun Aug 27 18:59:13 CDT 2017


This example still has nested closures (to create a Future), and still relies on a synchronous get method that will block a thread. Async/await does not require blocking any threads.

I’m definitely a fan of futures, but this example isn’t even a good example of using futures. If you’re using a synchronous get method then you’re not using futures properly. They’re supposed to make it easy to avoid writing blocking code. This example just does the blocking call on some other thread.

Doing it properly would show the benefits of async/await because it would require more nesting and more complex error handling. By simplifying the code you’ve made a comparison between proper asynchronous code (with async/await) and improper asynchronous code (your example).

That tendency to want to just block a thread to make it easier is exactly why async/await is so valuable. You get simple code while still doing it correctly. 

--
Adam Kemp

> On Aug 27, 2017, at 4:00 PM, Howard Lovatt via swift-evolution <swift-evolution at swift.org> wrote:
> 
> The running example used in the white paper coded using a Future is:
> 
> func processImageData1() -> Future<Image> {
>     return AsynchronousFuture { _ -> Image in
>         let dataResource  = loadWebResource("dataprofile.txt") // dataResource and imageResource run in parallel.
>         let imageResource = loadWebResource("imagedata.dat")
>         let imageTmp      = decodeImage(dataResource.get ?? Resource(path: "Default data resource or prompt user"), imageResource.get ?? Resource(path: "Default image resource or prompt user"))
>         let imageResult   =  dewarpAndCleanupImage(imageTmp.get ?? Image(dataPath: "Default image or prompt user", imagePath: "Default image or prompt user"))
>         return imageResult.get ?? Image(dataPath: "Default image or prompt user", imagePath: "Default image or prompt user")
>     }
> }
> 
> This also avoids the pyramid of doom; the pyramid is avoided by converting continuation-handlers into either a sync or future, i.e. it is the importer that eliminates the nesting by translating the code automatically. 
> 
> This example using Future also demonstrates three advantages of Future: they are naturally parallel (dataResource and imageResource lines run in parallel), they timeout automatically (get returns nil if the Future has taken too long), and if there is a failure (for any reason including timeout) it provides a method of either detecting the failure or providing a default (get returns nil on failure). 
> 
> There are a three of other advantages a Future has that this example doesn’t show: control over which thread the Future runs on, Futures can be cancelled, and debugging information is available.
> 
> You could imagine `async` as a syntax sugar for Future, e.g. the above Future example could be:
> 
> func processImageData1() async -> Image {
>     let dataResource  = loadWebResource("dataprofile.txt") // dataResource and imageResource run in parallel.
>     let imageResource = loadWebResource("imagedata.dat")
>     let imageTmp      = decodeImage(dataResource.get ?? Resource(path: "Default data resource or prompt user"), imageResource.get ?? Resource(path: "Default image resource or prompt user"))
>     let imageResult   =  dewarpAndCleanupImage(imageTmp.get ?? Image(dataPath: "Default image or prompt user", imagePath: "Default image or prompt user"))
>     return imageResult.get ?? Image(dataPath: "Default image or prompt user", imagePath: "Default image or prompt user")
> }
> 
> Since an async is sugar for Future the async runs as soon as it is created (as soon as the underlying Future is created) and get returns an optional (also cancel and status would be still be present). Then if you want control over threads and timeout they could be arguments to async:
> 
> func processImageData1() async(queue: DispatchQueue.main, timeout: .seconds(5)) -> Image { ... }
> 
>> On Sat, 26 Aug 2017 at 11:00 pm, Florent Vilmart <florent at flovilmart.com> wrote:
>> Howard, with async / await, the code is flat and you don’t have to unowned/weak self to prevent hideous cycles in the callbacks.
>> Futures can’t do that
>> 
>>> On Aug 26, 2017, 04:37 -0400, Goffredo Marocchi via swift-evolution <swift-evolution at swift.org>, wrote:
>>> With both he now built in promises in Node8 as well as libraries like Bluebird there was ample time to evaluate them and convert/auto convert at times libraries that loved callback pyramids of doom when the flow grows complex into promise based chains. Converting to Promises seems magical for the simple case, but can quickly descend in hard to follow flows and hard to debug errors when you move to non trivial multi path scenarios. JS is now solving it with their implementation of async/await, but the point is that without the full picture any single solution would break horribly in real life scenarios.
>>> 
>>> Sent from my iPhone
>>> 
>>> On 26 Aug 2017, at 06:27, Howard Lovatt via swift-evolution <swift-evolution at swift.org> wrote:
>>> 
>>>> My argument goes like this:
>>>> 
>>>>   1. You don't need async/await to write a powerful future type; you can use the underlying threads just as well, i.e. future with async/await is no better than future without. 
>>>> 
>>>>   2. Since future is more powerful, thread control, cancel, and timeout, people should be encouraged to use this; instead because async/await are language features they will be presumed, incorrectly, to be the best way, consequently people will get into trouble with deadlocks because they don't have control.
>>>> 
>>>>   3. async/await will require some engineering work and will at best make a mild syntax improvement and at worst lead to deadlocks, therefore they just don't carry their weight in terms of useful additions to Swift.
>>>> 
>>>> Therefore, save some engineering effort and just provide a future library.
>>>> 
>>>> To turn the question round another way, in two forms:
>>>> 
>>>>   1. What can async/wait do that a future can't?
>>>> 
>>>>   2. How will future be improved if async/await is added?
>>>> 
>>>> 
>>>>   -- Howard.
>>>> 
>>>>> On 26 August 2017 at 02:23, Joe Groff <jgroff at apple.com> wrote:
>>>>> 
>>>>>> On Aug 25, 2017, at 12:34 AM, Howard Lovatt <howard.lovatt at gmail.com> wrote:
>>>>>> 
>>>>>>  In particular a future that is cancellable is more powerful that the proposed async/await.
>>>>> 
>>>>> It's not more powerful; the features are to some degree disjoint. You can build a Future abstraction and then use async/await to sugar code that threads computation through futures. Getting back to Jakob's example, someone (maybe the Clang importer, maybe Apple's framework developers in an overlay) will still need to build infrastructure on top of IBActions and other currently ad-hoc signalling mechanisms to integrate them into a more expressive coordination framework.
>>>>> 
>>>>> -Joe
>>>> 
>>>> _______________________________________________
>>>> swift-evolution mailing list
>>>> swift-evolution at swift.org
>>>> https://lists.swift.org/mailman/listinfo/swift-evolution
> 
> -- 
> -- Howard.
> _______________________________________________
> 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/20170827/b19401e1/attachment.html>


More information about the swift-evolution mailing list