[Concurrency] async/await + actors

Great writeup. Here's a few comments about the manifesto, actors and value semantics specifically.

# About actors and data isolation

Can I call global functions like sin(x) in an actor? Surely you need to be able to do that. But how can the compiler know which functions are safe to use and which are out of bound for an actor?

Actors shouldn't access the global mutable state and thus should only call functions that do not access the global mutable state. We probably need a concept of a functions being pure (as in not accessing the global mutable state) for that.

# About the part "Does a type provide proper value semantics?"

I would argue that this is the wrong question. Most types are a hybrid form of value and reference semantics. All you need is to limit what you do to what is proper value semantics and forbid the parts that use reference semantics. Otherwise you are pessimising the capabilities of the types.

For instance, has Array<UIView> value semantics? You might be tempted to say that it does not because it contains class references, but in reality that depends on what you do with those UIViews. If you treat the class references as opaque pointers (never dereferencing them), you preserve value semantics. You can count the elements, shuffle them, all without dereferencing the UIViews it contains. Value semantics only end when you dereference the class references. And even then, there are some exceptions.

I suggested a little while ago on this list some principles based on this that would allow for the compiler to enforce value semantics with a `pure` attribute and I'm currently working on a draft proposal. In essence this proposal will have pure functions guaranteeing value semantics and no access to the global state, and a correct implementation for copy-on-write types. I think this would be useful for actors.

It’s great to hear that you’re returning to the topic of pure functions Michel! Please let me know if there is anything I can do to help.

···

On Aug 18, 2017, at 9:19 AM, Michel Fortin via swift-evolution <swift-evolution@swift.org> wrote:

Le 17 août 2017 à 18:24, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> a écrit :

Hi all,

As Ted mentioned in his email, it is great to finally kick off discussions for what concurrency should look like in Swift. This will surely be an epic multi-year journey, but it is more important to find the right design than to get there fast.

I’ve been advocating for a specific model involving async/await and actors for many years now. Handwaving only goes so far, so some folks asked me to write them down to make the discussion more helpful and concrete. While I hope these ideas help push the discussion on concurrency forward, this isn’t in any way meant to cut off other directions: in fact I hope it helps give proponents of other designs a model to follow: a discussion giving extensive rationale, combined with the long term story arc to show that the features fit together.

Anyway, here is the document, I hope it is useful, and I’d love to hear comments and suggestions for improvement:
Swift Concurrency Manifesto · GitHub

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

--
Michel Fortin
https://michelf.ca <https://michelf.ca/&gt;
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hi Chris,

I love what I’ve read so far. I just have one curiosity from my cursory look over the proposal.

It seems that in transitioning API to an Async Await system, the completion handler’s type becomes the return type. How would you handle bridging in Foundation API that already has a return type, eg URLSession returns you data tasks, and then fires a completion handler. Perhaps I missed something. I’m sure you could always form a tuple as well, just curious the thoughts on that.

Thanks,

Rod

···

On 18 Aug 2017, at 8:25 am, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 17, 2017, at 3:24 PM, Chris Lattner <clattner@nondot.org> wrote:

Hi all,

As Ted mentioned in his email, it is great to finally kick off discussions for what concurrency should look like in Swift. This will surely be an epic multi-year journey, but it is more important to find the right design than to get there fast.

I’ve been advocating for a specific model involving async/await and actors for many years now. Handwaving only goes so far, so some folks asked me to write them down to make the discussion more helpful and concrete. While I hope these ideas help push the discussion on concurrency forward, this isn’t in any way meant to cut off other directions: in fact I hope it helps give proponents of other designs a model to follow: a discussion giving extensive rationale, combined with the long term story arc to show that the features fit together.

Anyway, here is the document, I hope it is useful, and I’d love to hear comments and suggestions for improvement:
Swift Concurrency Manifesto · GitHub

Oh, also, one relatively short term piece of this model is a proposal for adding an async/await model to Swift (in the form of general coroutine support). Joe Groff and I wrote up a proposal for this, here:
Concrete proposal for async semantics in Swift · GitHub

and I have a PR with the first half of the implementation here:
Async await prototype by lattner · Pull Request #11501 · apple/swift · GitHub

The piece that is missing is code generation support.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Part 1: Async/await

    let dataResource = await loadWebResource("dataprofile.txt")

Was there any thought about introducing the concept of a timeout when awaiting?

Something like an `await for:` optional parameter?
Then, if used with try, it could go like


    let dataResource = try await for:10/*seconds*/ loadWebResource("dataprofile.txt”) catch _ as Timeout { //abort,retry cancel }

Timeouts should probably be handled at a higher level, but its just something that jumped out to me a little, since its something I notice sometimes people neglect to take care of
 :/

Something like this could definitely be built and added later: the sketch for async/await is intentionally kept as minimal as possible. That said, this seems like it is wandering into territory that would make sense to be API, rather than first class language feature.

Part 2: Actors
One other thing that also jumped at me was, if we are going to have actors, and having them encapsulate/isolate state, would it also make sense to make sure that we can’t invoke state changing messages when its invalid?

As an example, if we had a ”downloader” actor that downloads multiple files, we wouldn't want to be able to send invalid messages such as `begin` when downloading has already ”begun”
. Would it then make more sense to have callable messages be determined by the publicly visible state of the actor instead?

Yes, I completely agree and know what you’re talking about here. You’re unintentionally poking one of my other axes the grind. :)

This desire (to have conceptual “states” that a type moves through) is something that is super common. Files can be in “initialized” and “open” states, and that changes the valid actions that can be performed on them. A mutex can be locked or unlocked etc. Being able to express these invariants is highly related to pre and post conditions.

One abstraction that has been developed in the industry is the notion of typestate (https://en.wikipedia.org/wiki/Typestate_analysis\), which underlies common abstractions in static analyzer tools like the Clang Static Analyzer. I would love to see these ideas developed and built out at some point in time, and perhaps the next time I get a long block of time I’ll write another manifesto on those ideas ;-)

Coming back to your point, these ideas apply just as much to structs and classes as they do actors, so I think it should be a feature orthogonal to actors.

-Chris

···

On Aug 17, 2017, at 10:50 PM, Andre <pyunpyun@me.com> wrote:

# Async Await

I love this proposal so much. Much of it is exactly how I’ve thought
Swift’s concurrency model should look over the last year.

Making async a return attribute just like `throws` seems like the right
solution to me. Building on top of callbacks (rather than introducing
futures/promises) is also the right approach for swift. I think this
proposal nails the problem right on the head: callbacks don't work well
with the rest of Swift's error handling, is awkward, error prone, and yes,
looks ugly.

One point that I've gone back and forth on is how strictly to enforce
excecution order. For instance, in this example. it would make sense to
allow the first 2 lines to excecute in parallel and excecute the third line
once they both complete:

let a = await foo()
let b = await bar()
return [a, b]

But not in this case:

await client.connect()
let rooms = await client.getRooms()

In the first case the compiler could automatically optimize to run in
parallel, the second, it could not. I like the idea of wrapping parallel
code in a future, making the operation explicit and clear.

### Excecution context

I’m not familiar with C# or other implementations of async/await, only with
Javascript’s (very new) implementation. I’m curious how C# handles
execution contexts (threads, queue etc) since JS doesn’t have to deal with
that.

The `syncCoroutine` and `asyncCoroutine` example seems weird to me. It's
also unclear in that example what the context would be after a call to
async. Would excecution return to the queue, or be on whatever queue the
async function called back on? It makes a lot more sense to me to represent
this with code blocks, something like:

doSomeStuff()
await startAsync(mainQueue) {
doSomeStuffOnMainThread()
}
await startAsync(backgroundQueue) {
doSomeStuffInBackground()
}

Where every line in the code block is run on the context. This doesn't
handle synchronous excecution though. For instance, if we wanted to block a
queue until the entire async function had returned. An alternative might be
to have queues and other contexts define their own method that took async
functions:

doSomeStuff()
mainQueue.sync {
await loadSomethingStartingOnMain()
doSomeStuffOnMainThread()
// don't let anything else exceute on the main queue until this line
}
await mainQueue.async {
doSomeStuffInBackground()
}

Using `queue.async` taking an async function might be a good alternative to
a language "startAsync". Having the excecution context undefined based on
whatever queue the underlying code calls back on seems dangerous to me.
Forcing the user to define the context would fix that, but at the cost of
introducing extra dispatch calls where they may not be needed. A general
purpose context, that simply continued excecution in place would fix that,
and be more explicit when it was needed.

## Conversion of imported Objective-C APIs

One issue I see with the importer is that the conventions for callbacks
aren’t as strict as NSError ** methods were. For instance, URLSession
completion blocks include response, data and error, all of which are
optionals. The response is almost always present, even if there was an
error. But there is no way to know that from the ObjC type system, and no
way to represent a throwing function that also returns metadata on error in
Swift.

There are also cases where you wouldn’t want ObjC callbacks to be imported
as async functions. For instance, it wouldn’t make sense for
NotificationCenter callbacks to be awaited. In general, any callback that
can be called more than once is dangerous to use as an async function.

Personally, I would be in favor of taking a reserved but default on
approach to importing ObjC functions as async, and adding annotations to
ObjC to control their Swift importing. For instance, by default callbacks
with either 0 or 1 arguments would be imported as async non-throwing and
callbacks with 0 or 1 arguments plus an error would be imported as throwing
async. Callbacks with more than 1 argument would need to be manually
annotated. Methods that should not be async (like NotificationCenter) can
be annotated to not be imported as async.

Another issue we’ll need to contend with is intermediate tasks. Both
URLSession and the Photos framework come to mind. In the existing model,
they return something that allows you to cancel the request while it is in
progress. Consider the following example:

class ImageView {
private var currentTask: URLSessionTask?
var source: URL? {
didSet {
currentTask?.cancel()
image = nil
guard let source = self.source else { return }
load(source: source) { image, error in
guard self.source == source else { return }
if let image = image {
self.image = image
} else {
self.image = errorImage
}
}
}
}
var image: Image?
func load(source: URL, completion: @escaping (Image?, Error?) -> Void) {
let task = URLSession.shared.dataTask(with: source) { (data, response,
error) in
guard let data = data else {
completion(nil, error)
return
}
let image = UIImage(data: data)
completion(image, nil)
}
self.currentTask = task
task.resume()
}
}

How should we represent dataTask(with:completion:)? If I were writing this
API from scratch, task.resume() would be the async function, but that may
not be feasible for the importer.

If I could rewrite that example in a magical future version of Swift and
Foundation, it would look something like this:

class ImageView {
private var currentTask: URLSessionTask?
var source: URL? {
didSet {
currentTask?.cancel()
image = nil
guard let source = self.source else { return }
startAsync {
do {
let image = await try load(source: source)
guard self.source == source else { return }
self.image = image
} catch {
guard self.source == source else { return } // kind of awkward to have to
write this twice
self.image = errorImage
}
}
}
}
var image: Image?
func load(source: URL) async throws -> Image {
let task = URLSession.shared.dataTask(with: source)
self.currentTask = task
let data = await try task.resume()
return await UIImage(data: data)
}
}

## Actors

I fail to see the benefit of adding an entirely new construct for the actor
model. It seems like access control, value semantics, and dispatch queues
largely fill this need already, and the implimentation of an actor wouldn't
be so complicated currently that it needs a compiler feature to ensure a
correct implimentation.

Futher, it seems like giving up strict actor models would remove some of
the benefits other languages have with their actor models. It has been quit
a while since I worked with Erlang, but from what I remember, the magic of
it's recovery model comes from it's recursive, functional model. It can
recover from otherwise fatal errors because it can just reset it's state to
the state before the last message was received. For reasons outlined
already, Swift can't enforce that strictly, so no matter what we are going
to have to rely on implimentations to be implimented correctly.

Perhaps I'm missing something though.

···

On Thu, Aug 17, 2017 at 3:25 PM, Chris Lattner via swift-evolution < swift-evolution@swift.org> wrote:

On Aug 17, 2017, at 3:24 PM, Chris Lattner <clattner@nondot.org> wrote:

Hi all,

As Ted mentioned in his email, it is great to finally kick off discussions
for what concurrency should look like in Swift. This will surely be an
epic multi-year journey, but it is more important to find the right design
than to get there fast.

I’ve been advocating for a specific model involving async/await and actors
for many years now. Handwaving only goes so far, so some folks asked me to
write them down to make the discussion more helpful and concrete. While I
hope these ideas help push the discussion on concurrency forward, this
isn’t in any way meant to cut off other directions: in fact I hope it helps
give proponents of other designs a model to follow: a discussion giving
extensive rationale, combined with the long term story arc to show that the
features fit together.

Anyway, here is the document, I hope it is useful, and I’d love to hear
comments and suggestions for improvement:
Swift Concurrency Manifesto · GitHub

Oh, also, one relatively short term piece of this model is a proposal for
adding an async/await model to Swift (in the form of general coroutine
support). Joe Groff and I wrote up a proposal for this, here:
Concrete proposal for async semantics in Swift · GitHub

and I have a PR with the first half of the implementation here:
Async await prototype by lattner · Pull Request #11501 · apple/swift · GitHub

The piece that is missing is code generation support.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Here's my thoughts:

- You might have been too soft on the callback-based syntax of many Cocoa APIs: those are really bad, and in general a pain to use. Of course a import strategy for future Swift will be needed, but I wouldn't consider design constraints exclusively derived from some Cocoa APIs shortcomings, like URLSession.dataTask(withURL:completionHandler:) that I think can be safely ignored.

I don’t think the design is being constrained by Cocoa, I look at it as the right answer which conveniently dovetails almost perfectly with Cocoa - similar to when Swift 2’s error handling mechanic absorbed the NSError patterns. In the case of error handling, the most common uses were absorbed directly, but the odd cases were not. I expect the same to happen here if async/await is adopted.

- I agree on the focus on async/await as compiler-level tools for defining and using coroutines, with Future<T> and stuff like that to be considered as library functions built on top of the syntax: this way async/await doesn't become mere syntactic sugar.

- I really STRONGLY disagree on conflating async and throws: they are different things, and exist for different purposes, the same way as Future<T> and Result<T> are different things and should be usable separately. The right way to handle a "failable" future is to simply use a Future<Result<T>>, and eventually define convenience functions for the "success" and "failure" cases, or event methods on Future (in this case a ResultType protocol would be needed). It's a lot better to define simpler concepts that compose in interesting ways, rather than conflating unrelated things for minor conveniences.

I understand your concern, but other communities (e.g. Rust and C#) have chosen to conflate them out of practicality. Joe Groff has a lot to say about this, hopefully he chimes in.

- async/await is good, but the Actor model part is simply glorious :D

- I'm so happy that adding a native idiomatic Actor model has been considered for Swift, and I like the interplay with await for making actor functions return, as well as the progressive disclosure that can be achieved with more and more restrictive keywords: it even seems to me a real step-up from existing implementations of the model.

- in general I think that one of the best (if not the best) features of Swift is the idea of progressive disclosure, and that should be preserved... and I'm a fan of "actor class" :D

- I like the "ValueSemantical" protocol idea, but eventually I would like Swift to have features to actually enforce safe patterns though the language itself, like a "pure" or "safe" keyword: I almost use no classes (excluding the UIKit ones that I'm forced to use) and mostly write pure functions, but as the language forces me to do extra work when I'm dealing with an Optional or a throwing function, and that's a good thing in my opinion and facilitates my work as a software engineer, I would love a language that warned me that I'm accessing a potentially mutable instance with reference semantics in a context that I'm mistakenly considering as pure, or that the function I'm writing is not actually pure because a non-pure function was called inside it.

Understood. There are other interesting and potentially related opportunities that could dovetail here. Several people have discussed adding a first class COW concept to the language to easy the implementation of COW types (here’s one proposal: https://github.com/eeckstein/swift/blob/cow-proposal/docs/proposals/CopyOnWrite.rst\). Something like that could help.

- I love the way the "actor" keyword for a method transforms it in an "inbox": I think the implementation you're proposing is super "Swifty" and could appear so convenient that many people could be drawn to the language just thanks to the great native concurrency model; I think that a powerful but simple and usable concurrency model is a heavy motivation for adopting a certain language in many contexts.

Awesome!

-Chris

···

On Aug 18, 2017, at 6:50 AM, Elviro Rocca <retired.hunter.djura@gmail.com> wrote:

An argument could be made that throws should precede the function
definition, but since that ship has sailed, async should probably join it
at the end, since it modifies the way the function returns. In other words,
async doesn't change how the method gets run, but how and when it returns.

···

On Thu, Aug 17, 2017 at 11:34 PM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

> On Aug 17, 2017, at 3:24 PM, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:
>
> Anyway, here is the document, I hope it is useful, and I’d love to hear
comments and suggestions for improvement:
> Swift Concurrency Manifesto · GitHub

I think you're selecting the right approaches and nailing many of the
details, but I have a lot of questions and thoughts. A few notes before I
start:

* I only did one pass through this, so I probably missed or misunderstood
some things. Sorry.
* I think the document may have evolved since I started reading, so some
of this may be out of date.
* I haven't yet read the rest of the thread—this email is already long
enough.
* I have a lot of experience with Cocoa-style callback-based concurrency,
a little bit (unfortunately) with Javascript Promises, and basically none
with async/await. I've never worked with a language that formally supported
actors, although I've used similar patterns in Swift and Objective-C.

# async/await

I like the choice of async/await, and I agree that it's pretty much where
mainstream languages have ended up. But there are a few things you seem to
gloss over. You may simply have decided those details were too specific for
such a sweeping manifesto, but I wanted to point them out in case you
missed them.

## Dispatching back to the original queue

You correctly identify one of the problems with completion blocks as being
that you can't tell which queue the completion will run on, but I don't
think you actually discuss a solution to that in the async/await section.
Do you think async/await can solve that? How? Does GCD even have the
primitives needed? (`dispatch_get_current_queue()` was deprecated long
ago and has never been available in Swift.)

Or do you see this as the province of actors? If so, how does that work?
Does every piece of code inherently run inside one actor or another? Or
does the concurrency system only get you on the right queue if you
explicitly use actors? Can arbitrary classes "belong to" an actor, so that
e.g. callbacks into a view controller inherently go to the main queue/actor?

(If you *do* need actors to get sensible queue behavior, I'm not the
biggest fan of that; we could really use that kind of thing in many, many
places that aren't actors.)

## Delayed `await`

Most languages I've seen with async/await seem to allow you to delay the
`await` call to do parallel work, but I don't see anything similar in your
examples. Do you envision that happening? What's the type of the
intermediate value, and what can you do with it? Can you return it to a
caller?

## Error handling

Do you imagine that `throws` and `async` would be orthogonal to one
another? If so, I suspect that we could benefit from adding typed `throws`
and making `Never` a subtype of `Error`, which would allow us to handle
this through the generics system.

(Also, I notice that a fire-and-forget message can be thought of as an
`async` method returning `Never`, even though the computation *does*
terminate eventually. I'm not sure how to handle that, though)

## Legacy interop

Another big topic I don't see discussed much is interop with existing
APIs. I think it's really important that we expose existing
completion-based Cocoa APIs with async/await. This ideally means automatic
translation, much like we did with errors. Moreover, I think we probably
need to apply this translation to Swift 4 libraries when you're using them
from Swift 5+ (assuming this makes Swift 5).

## Implementation

The legacy interop requirement tends to lean towards a particular model
where `await` calls are literally translated into completion blocks passed
to the original function. But there are other options, like generating a
wrapper that translates calls with completions into calls returning
promises, and `await` is translated into a promise call. Or we could do
proper continuations, but as I understand it, that has impacts further up
the call stack, so I'm not sure how you'd make that work when some of the
calls on the stack are from other languages.

# Actors

I haven't used actors before, but they look like a really promising model,
much better than Go's channels. I do have a couple of concerns, though.

## Interop, again

There are a few actor-like types in the frameworks—the WatchKit UI classes
are the clearest examples—but I'm not quite worried about them. What I'm
more concerned with is how this might interoperate with Cocoa delegates.
Certain APIs, like `URLSession`, either take a delegate and queue or take a
delegate and call it on arbitrary queues; these seem like excellent
candidates for actor-ization, especially when the calls are all one-way.
But that means we need to be able to create "actor protocols" or something.
It's also hard to square with the common Cocoa (anti?)pattern of
implementing delegate protocols on a controller—you would want that
controller to also be an actor.

I don't have any specific answers here—I just wanted to point this out as
something we should consider in our actor design.

## Value-type annotation

The big problem I see with your `ValueSemantical` protocol is that
developers are very likely to abuse it. If there's a magic "let this get
passed into actors" switch, programmers will flip it for types that don't
really qualify; we don't want that switch to have too many other effects.
I also worry that the type behavior of a protocol is a bad fit for
`ValueSemantical`. Retroactive conformance to `ValueSemantical` is almost
certain to be an unprincipled hack; subclasses can very easily lose the
value-semantic behavior of their superclasses, but almost certainly can't
have value semantics unless their superclasses do. And yet having
`ValueSemantical` conformance somehow be uninherited would destroy Liskov
substitutability.

One answer might be to narrow the scope of the annotation: Don't think of
it as indicating that it's a value type, merely think of it as a
"passable-to-`Actor`s" protocol. I'll call this alternate design `Actable`
to distinguish it from "is a value type". It's not an unprincipled hack to
retroactively conform a type to `Actable`—you're not stating an intrinsic
property of your type, just telling the actor system how to pass it. It's
totally coherent to have a subclass of a non-`Actable` class add `Actable`
and require its own subclasses to be `Actable`. And we can still synthesize
`Actable` on structs and enums.

A middle ground would be to define the protocol as being for types which
can be safely passed to another thread—`Shareable`, say. That might even
permit implementations that used atomics or mutexes to protect a shared
instance.

(Sorry if this comes off as bikeshedding. What I'm trying to say is, while
the exact name is unimportant, the semantic we want the protocol to
represent *is* important. I suspect that "has value semantics" is too broad
and will lead users into misbehavior.)

## Plain old classes

In the section on actors, you suggest that actors can either be a variant
of classes or a new fundamental type, but one option you don't seem to
probe is that actors could simply *be* subclasses of an `Actor` class:

        class Storage: Actor {
                func fetchPerson(with uuid: UUID) async throws -> Person? {
                        ...
                }
        }

You might be able to use different concurrency backends by using different
base classes (`GCDActor` vs. `DillActor` vs. whatever), although that would
have the drawback of tightly coupling an actor class to its backend.
Perhaps `Actor` could instead be a generic class which took an
`ActorBackend` type parameter; subclasses could either fix that parameter
(`Actor<DispatchQueue>`) or expose it to their users.

Another possibility doesn't involve subclasses at all. In this model, an
actor is created by an `init() async` initializer. An async initializer on
`Foo` returns an instance of type `Foo.Async`, an implicitly created
pseudo-class which contains only the `async` members of `Foo`.

        class Storage {
                let context: NSManagedObjectContext

                init(url: URL) async throws {
                        // ...build a Core Data stack...
                        context = NSManagedObjectContext(concurrencyType:
.privateQueueConcurrencyType)
                        context.persistentStoreCoordinator = coordinator
                }

                func fetchPerson(with uuid: UUID) async throws -> Person? {
                        let req = NSFetchRequest<NSManagedObject>(entityType:
"Person")
                        req.predicate = NSPredicate(format: "uuid = %@",
uuid)
                        req.fetchLimit = 1

                        return execute(req, for: Person.self).first
                }

                func execute<R: RecordConvertible>(_ req: NSFetchRequest<NSManagedObject>,
for type: R.Type) throws -> [R] {
                        let records = try context.fetch(req)
                        return try records.map { try Person.init(record:
$0 as! NSManagedObject) }
                }
        }

        let store: Storage.Async = await try Storage(url: url)

        // This is okay because `fetchPerson(with:)` is `async`.
        let person = await try store.fetchPerson(with: personID)

        // This is an error because `execute(_:for:)` is not `async`,
        // so it's not exposed through `Storage.Async`.
        let people = try store.execute(req, for: Person.self)

A third possibility is to think of the actor as a sort of proxy wrapper
around a (more) synchronous class, which exposes only `actor`-annotated
members and wraps calls to them in serialization logic. This would require
some sort of language feature to make transparent wrappers, though. This
design would allow the user, instead of the actor, to select a "backend"
for it, so an iOS app could use `GCDActor<Storage>` while its server
backend could use `DillActor<Storage>`. (`Storage` is a bad example for
shared code, but you get the idea.)

My point here is simply that, although you show the actor-ness of a type
as being fundamental to it, I'm not sure it needs to be.

### Lifting parameter type restrictions into `async`

The major downside of an "actors are not special types" model is that it
wouldn't enforce the parameter type restrictions. One solution would be to
apply those restrictions to *all* `async` functions—their parameters would
all have to conform to the magic "okay for actors" protocol (well, it'd be
"okay for async" now). That strikes me as a pretty sane restriction, since
the shared-state problems we want to avoid with actors are also
questionable with other async calls.

However, this would move the design of the magic protocol forward in the
schedule, and might delay the deployment of async/await. If we *want* these
restrictions on all async calls, that might be worth it, but if not, that's
a problem.

We'd probably also need to provide an escape hatch—either a function-wide
`async(unsafelyShared)` annotation, or a per-parameter `@unsafelyShared`
attribute.

## Function-typed parameters

You mention that function types would be unsafe to pass "because it could
close over arbitrary actor-local data", but closures over non-shared data
would be fine. Another carve-out that I *think* we could support is `async`
functions in general, because if they were closures, they could close over
their original actor and run inside it. This might be able to subsume the
"closure over non-shared data" case.

## The inevitable need for metadata

GCD started with a very simple model: you put blocks on a queue and the
queue runs them in order. This was much more lightweight than
`NSOperationQueue`, which had a lot of extra stuff for canceling
operations, prioritizing them, etc. Unfortunately, within a few years Apple
decided that GCD *needed* to be able to cancel and prioritize operations,
so they had to pack this information into weird pseudo-block objects. In
Swift, this manifested as the `DispatchWorkItem` class.

My point is, in anything that involves background processing, you always
end up needing more configurability than you think at the start. We should
anticipate this in our design and have a plan for how we'll attach metadata
to actor messages, even if we don't implement that feature right away.
Because we'll surely need to sooner or later.

## Examples

In a previous section, I used a class called `Storage` as an actor; I
think that might be a good type to illustrate with. I envision this as a
type that translates between the Swift structs/enums you use in your model
layer and the REST server/SQLite database/Core Data stack/CloudKit database
you use to actually store it.

Other examples might include:

* A shared cache:

   actor SharedCache<Key: Hashable, Value> {
       private var values: [Key: Value]

       actor func cachedValue(for key: Key, orMake makeValue: (Key) async
throws -> Value) rethrows -> Value {
           if let value = values[key] {
               return value
           }

           let value = try await makeValue(key)
           values[key] = value
           return value
       }
   }

* A spell checker:

   actor SpellChecker {
       private let words: Set<String>

       actor func addWord(_ word: String) throws {
                words.insert(word)
                await save()
        }
       actor func removeWord(_ word: String) throws {
                words.remove(word)
                await save()
        }

        func save() async throws { ... }

       actor func checkText(_ text: String) -> Checker {
           return Check(words: words, text: text, startIndex:
text.startIndex)
       }

       actor Checker /* Hmm, can we get a SequenceActor and `for await`
loop? */ {
           fileprivate let words: Set<String>
           fileprivate let text: String
           fileprivate var startIndex: String.Index

           actor func next() -> Misspelling? { ... }
       }

       struct Misspelling: ValueSemantical {
           var substring: Substring
           var corrections: [String]
       }
   }

# Reliability

Overall, I like reliability at the actor level; it seems like an
appropriate unit of trap-resistance.

I don't think we should incorporate traps into normal error-handling
mechanisms; that is, I don't think resilient actors should throw on traps.
When an invariant is violated within an actor, that means *something went
wrong* in a way that wasn't anticipated. The mistake may be completely
internal to the actor in question, but it may also have stemmed from
invalid data passed into it—data which may be present in other parts of the
system. In other words, I don't think we should think of reliable actors as
a way to normalize trapping; we should think of it as a way to mitigate the
damage caused by a trap, to trap gracefully. Failure handlers encourage the
thinking we want; throwing errors encourages the opposite.

To that end, I think failure handlers are the right approach. I also think
we should make it clear that, once a failure handler is called, there is no
saving the process—it is *going* to crash eventually. Maybe failure
handlers are `Never`-returning functions, or maybe we simply make it clear
that we're going to call `fatalError` after the failure handler runs, but
in either case, a failure handler is a point of no return.

(In theory, a failure handler could keep things going by pulling some
ridiculous shenanigans, like re-entering the runloop. We could try to
prevent that with a time limit on failure handlers, but that seems like
overengineering.)

I have a few points of confusion about failure handlers, though:

1. Who sets up a failure handler? The actor that might fail, or the actor
which owns that actor?

2. Can there be multiple failure handlers?

3. When does a failure handler get invoked? Is it queued like a normal
message, or does it run immediately? If the latter, and if it runs in the
context of an outside actor, how do we deal with the fact that invariants
might not currently hold?

# Distributed actors

I love the feature set you envision here, but I have two major criticisms.

## Heterogeneity is the rule

Swift everywhere is a fine idea, but heterogeneity is the reality. It's
the reality today and it will probably be the reality in twenty years. A
magic "distributed actor" model isn't going to do us much good if it
doesn't work when the actor behind it is implemented in Node, PHP, or Java.

That means that we should expect most distributed actors to be wrappers
around marshaling code. Dealing with things like XPC or Neo-Distributed
Objects is great, but we also need to think about "distributed actors"
based on `JSONEncoder`, `URLSession`, and some custom glue code to stick
them together. That's probably most of what we'll end up doing.

## It's just a tweaked backend

You describe this as a `distributed` keyword, but I don't think the
keyword actually adds much. I don't think there's a simple, binary
distinction between distributed and non-distributed actors. Rather, there
are a variety of actor "backends"—some in-process, some in-machine, some
in-network—which vary in two dimensions:

1. **Is the backend inherently error-prone?** Basically, should actor
methods that normally are not `throws` be exposed as `throws` methods
because the backend itself is expected to introduce errors in the normal
course of operation?

2. **How strictly does the backend constrain the types of parameters you
can pass?** In-process, anything that can be safely used by multiple
threads is fine. In-machine, it needs to be `Codable` or support `mmap`ing.
In-network, it needs to be `Codable`. But that's only the common case, of
course! A simple in-machine backend might not support `mmap`; a
sophisticated in-network backend might allow you to pass one of your
`Actor`s to the other side (where calls would be sent back the other way).

Handling these two dimensions of variation basically requires new protocol
features. For the error issue, we basically need typed `throws`, `Never` as
a universal subtype (or at least a universal subtype of all `Error`s), and
an operation equivalent to `#commonSupertype(BackendError, MethodError)`.
For the type-constraining issue, we need an "associated protocol" feature
that allows you to constrain `ActorBackend`'s parameters to a protocol
specified by the conforming type. And, y'know, a way to reject
actor/backend combinations that aren't compatible.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Great writeup. Here's a few comments about the manifesto, actors and value semantics specifically.

# About actors and data isolation

Can I call global functions like sin(x) in an actor? Surely you need to be able to do that. But how can the compiler know which functions are safe to use and which are out of bound for an actor?

The design I outline allows you to call it, and punts on the problem of safety:

Actors shouldn't access the global mutable state and thus should only call functions that do not access the global mutable state. We probably need a concept of a functions being pure (as in not accessing the global mutable state) for that.

I’m not aware of a practical model to achieve that, but if there were, more safety would provide more goodness :-)

# About the part "Does a type provide proper value semantics?"

I would argue that this is the wrong question. Most types are a hybrid form of value and reference semantics. All you need is to limit what you do to what is proper value semantics and forbid the parts that use reference semantics. Otherwise you are pessimising the capabilities of the types.

For instance, has Array<UIView> value semantics?

By the commonly accepted definition, Array<UIView> does not provide value semantics.

You might be tempted to say that it does not because it contains class references, but in reality that depends on what you do with those UIViews.

An aspect of the type (“does it have value semantics or not”) should not depend on the clients. By your definition, every type has value semantics if none of the mutating operations are called :-)

If you treat the class references as opaque pointers (never dereferencing them), you preserve value semantics. You can count the elements, shuffle them, all without dereferencing the UIViews it contains. Value semantics only end when you dereference the class references. And even then, there are some exceptions.

I agree with you that the model could permit all values to be sent in actor messages, but doing so would give up the principle advantages of mutable state separation. You’d have to do synchronization, you’d have the same bugs that have always existed, etc.

-Chris

···

On Aug 18, 2017, at 7:19 AM, Michel Fortin <michel.fortin@michelf.ca> wrote:

I am very interested in the async/await proposal. I will say up front that I have experience using C#’s async/await (with Cocoa via Xamarin) so I am biased towards what I am familiar with. That said, I think there are some advantages to the way C# does it that the current proposal is missing out on. One of the differences was touched on in Brent’s question here:

## Dispatching back to the original queue

You correctly identify one of the problems with completion blocks as being that you can't tell which queue the completion will run on, but I don't think you actually discuss a solution to that in the async/await section. Do you think async/await can solve that? How? Does GCD even have the primitives needed? (`dispatch_get_current_queue()` was deprecated long ago and has never been available in Swift.)

Or do you see this as the province of actors? If so, how does that work? Does every piece of code inherently run inside one actor or another? Or does the concurrency system only get you on the right queue if you explicitly use actors? Can arbitrary classes "belong to" an actor, so that e.g. callbacks into a view controller inherently go to the main queue/actor?

(If you *do* need actors to get sensible queue behavior, I'm not the biggest fan of that; we could really use that kind of thing in many, many places that aren't actors.)

I see this as one of the key advantages to the C# async/await model. One of the goals is to reduce the cognitive load of people writing async code such that you can more easily write correct code while writing in a straightforward way. Being on the right synchronization context without having to think too hard about it is a key part of that.

For instance, say you’re handling a button click, and you need to do a network request and then update the UI. In C# (using Xamarin.iOS as an example) you might write some code like this:

private async void HandleButtonClick(object sender, EventArgs e) {
    var results = await GetStuffFromNetwork();
    UpdateUI(results);
}

This event handler is called on the UI thread, and the UpdateUI call must be done on the UI thread. The way async/await works in C# (by default) is that when your continuation is called it will be on the same synchronization context you started with. That means if you started on the UI thread you will resume on the UI thread. If you started on some thread pool then you will resume on that same thread pool.

If you didn’t have that guarantee then every time you write “await" you would have to think about where that continuation code should run, and you would have to explicitly make that happen. That might mean something like this:

private async void HandleButtonClick(object sender, EventArgs e) {
    var results = await GetStuffFromNetworkAsync();
    BeginInvokeOnMainThread(() => {
        UpdateUI(results);
    });
}

That code doesn’t look much different from the non async/await code:

private void HandleButtonClick(object sender, EventArgs e) {
    GetStuffFromNetwork(results => {
        BeginInvokeOnMainThread(() => {
            UpdateUI(results);
        });
    });
}

Error handling would only complicate it further.

I feel like this is a very important aspect of how async/await simplifies asynchronous programming, and it is especially valuable in application development, which is a very important use case for Swift. Obviously GCD is a bit different than C#’s Task Parallel Library model, but I feel like there must be some way to reconcile these models.

···

On Aug 17, 2017, at 11:34 PM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

####

Another difference between the C# implementation and this proposal is the lack of futures. While I think it’s fair to be cautious about tying this proposal to any specific futures implementation or design, I feel like the value of tying it to some concept of futures was somewhat overlooked. For instance, in C# you could write a method with this signature:

public Task<int> GetIntAsync();

Consumers could then call that using await:

int i = await GetIntAsync();

Or they could just get the Task and pass it off to someone else:

Task<int> t = GetIntAsync();
return t;

Or they could get the Task and await it, but do something in between:

Task<int> t = GetIntAsync();
// Do something
await t;

That’s more useful when shown like this:

List<Task<int>> calculations = inputs.Select(input => GetIntAsync(input)).ToList();
int results = await Task.WhenAll(calculations);

The benefit of connecting the async/await feature to the concept of futures is that you can mix and match this code freely. The current proposal doesn’t seem to allow this. If you wanted to implement that last example in Swift you would need to write GetIntAsync differently than if you were implementing the simple case (just awaiting the one call). With the C# implementation you don’t have to care about whether your caller is going to use await or use the futures API directly. It’s flexible, and that makes it powerful.

The good news is that C#’s implementation is not actually tied to a particular futures implementation. Task/Task<T> is the common implementation, but the language will support using await on any type that has a GetAwaiter method (even an extension method) that returns a type that has certain methods on it (read more here: await anything; - .NET Parallel Programming). You can use this to implement your own futures API that still interoperates with async/await.

I don’t see a reason that Swift can’t do something similar.

I would really like to see more exploration of these concepts because I feel like they are both very important pieces of what makes async/await work so well in C#.

Anyway, here is the document, I hope it is useful, and I’d love to hear comments and suggestions for improvement:
Swift Concurrency Manifesto · GitHub

I think you're selecting the right approaches and nailing many of the details, but I have a lot of questions and thoughts. A few notes before I start:

Thanks!

## Dispatching back to the original queue

You correctly identify one of the problems with completion blocks as being that you can't tell which queue the completion will run on, but I don't think you actually discuss a solution to that in the async/await section. Do you think async/await can solve that? How? Does GCD even have the primitives needed? (`dispatch_get_current_queue()` was deprecated long ago and has never been available in Swift.)

Async/await does not itself solve this - again think of async/await as sugar for completion handlers. However, a follow-on to this proposal would be another proposal that describes how existing ObjC completion handlers are imported. It would be surely controversial and is possibly unwise, but we could build magic into the thunks for those. This is described here:

Iff it were a good idea to do this, we can work with the GCD folks to figure out the best implementation approach, including potentially new API.

## Error handling

Do you imagine that `throws` and `async` would be orthogonal to one another? If so, I suspect that we could benefit from adding typed `throws` and making `Never` a subtype of `Error`, which would allow us to handle this through the generics system.

Great question, I explore some of it here:

(Also, I notice that a fire-and-forget message can be thought of as an `async` method returning `Never`, even though the computation *does* terminate eventually. I'm not sure how to handle that, though)

Yeah, I think that actor methods deserve a bit of magic:

- Their bodies should be implicitly async, so they can call async methods without blocking their current queue or have to use beginAsync.
- However, if they are void “fire and forget” messages, I think the caller side should *not* have to use await on them, since enqueuing the message will not block.

If the actor method throws an error or returns a value, then yes, you'd have to await.

## Interop, again

There are a few actor-like types in the frameworks—the WatchKit UI classes are the clearest examples—but I'm not quite worried about them. What I'm more concerned with is how this might interoperate with Cocoa delegates. Certain APIs, like `URLSession`, either take a delegate and queue or take a delegate and call it on arbitrary queues; these seem like excellent candidates for actor-ization, especially when the calls are all one-way. But that means we need to be able to create "actor protocols" or something. It's also hard to square with the common Cocoa (anti?)pattern of implementing delegate protocols on a controller—you would want that controller to also be an actor.

I don't have any specific answers here—I just wanted to point this out as something we should consider in our actor design.

As part of the manifesto, I’m not proposing that existing APIs be “actorized”, though that is a logical thing to look into once the basic model is nailed down.

## Value-type annotation

The big problem I see with your `ValueSemantical` protocol is that developers are very likely to abuse it. If there's a magic "let this get passed into actors" switch, programmers will flip it for types that don't really qualify; we don't want that switch to have too many other effects.

I agree. That is one reason that I think it is important for it to have a (non-defaulted) protocol requirement. Requiring someone to implement some code is a good way to get them to think about the operation
 at least a little bit. That said, the design does not try to *guarantee* memory safety, so there will always be an opportunity for error.

I also worry that the type behavior of a protocol is a bad fit for `ValueSemantical`. Retroactive conformance to `ValueSemantical` is almost certain to be an unprincipled hack; subclasses can very easily lose the value-semantic behavior of their superclasses, but almost certainly can't have value semantics unless their superclasses do. And yet having `ValueSemantical` conformance somehow be uninherited would destroy Liskov substitutability.

Indeed. See NSArray vs NSMutableArray.

OTOH, I tend to think that retroactive conformance is really a good thing, particularly in the transition period where you’d be dealing with other people’s packages who haven’t adopted the model. You may be adopting it for their structs afterall.

An alternate approach would be to just say “no, you can’t do that. If you want to work around someone else’s problem, define a wrapper struct and mark it as ValueSemantical”. That design could also work.

## Plain old classes

In the section on actors, you suggest that actors can either be a variant of classes or a new fundamental type, but one option you don't seem to probe is that actors could simply *be* subclasses of an `Actor` class:

  class Storage: Actor {
    func fetchPerson(with uuid: UUID) async throws -> Person? {
      ...
    }
  }

You might be able to use different concurrency backends by using different base classes (`GCDActor` vs. `DillActor` vs. whatever), although that would have the drawback of tightly coupling an actor class to its backend. Perhaps `Actor` could instead be a generic class which took an `ActorBackend` type parameter; subclasses could either fix that parameter (`Actor<DispatchQueue>`) or expose it to their users.

Yes, that is possible, I’ll mention it.

Another possibility doesn't involve subclasses at all. In this model, an actor is created by an `init() async` initializer. An async initializer on `Foo` returns an instance of type `Foo.Async`, an implicitly created pseudo-class which contains only the `async` members of `Foo`.

...

A third possibility is to think of the actor as a sort of proxy wrapper around a (more) synchronous class, which exposes only `actor`-annotated members and wraps calls to them in serialization logic. This would require some sort of language feature to make transparent wrappers, though. This design would allow the user, instead of the actor, to select a "backend" for it, so an iOS app could use `GCDActor<Storage>` while its server backend could use `DillActor<Storage>`. (`Storage` is a bad example for shared code, but you get the idea.)

My point here is simply that, although you show the actor-ness of a type as being fundamental to it, I'm not sure it needs to be.

It would be a perfectly valid design approach to implement actors as a framework or design pattern instead of as a first class language feature. You’d end up with something very close to Akka, which has provides a lot of the high level abstractions, but doesn’t nudge coders to do the right thing w.r.t. shared mutable state enough (IMO).

### Lifting parameter type restrictions into `async`

The major downside of an "actors are not special types" model is that it wouldn't enforce the parameter type restrictions.

Right.

One solution would be to apply those restrictions to *all* `async` functions—their parameters would all have to conform to the magic "okay for actors" protocol (well, it'd be "okay for async" now). That strikes me as a pretty sane restriction, since the shared-state problems we want to avoid with actors are also questionable with other async calls.

However, this would move the design of the magic protocol forward in the schedule, and might delay the deployment of async/await. If we *want* these restrictions on all async calls, that might be worth it, but if not, that's a problem.

I’m not sure it make sense either given the extensive completion handler based APIs, which take lots of non value type parameters.

## The inevitable need for metadata

GCD started with a very simple model: you put blocks on a queue and the queue runs them in order. This was much more lightweight than `NSOperationQueue`, which had a lot of extra stuff for canceling operations, prioritizing them, etc. Unfortunately, within a few years Apple decided that GCD *needed* to be able to cancel and prioritize operations, so they had to pack this information into weird pseudo-block objects. In Swift, this manifested as the `DispatchWorkItem` class.

My point is, in anything that involves background processing, you always end up needing more configurability than you think at the start. We should anticipate this in our design and have a plan for how we'll attach metadata to actor messages, even if we don't implement that feature right away. Because we'll surely need to sooner or later.

Agreed.

# Reliability

Overall, I like reliability at the actor level; it seems like an appropriate unit of trap-resistance.

I don't think we should incorporate traps into normal error-handling mechanisms; that is, I don't think resilient actors should throw on traps. When an invariant is violated within an actor, that means *something went wrong* in a way that wasn't anticipated. The mistake may be completely internal to the actor in question, but it may also have stemmed from invalid data passed into it—data which may be present in other parts of the system. In other words, I don't think we should think of reliable actors as a way to normalize trapping; we should think of it as a way to mitigate the damage caused by a trap, to trap gracefully. Failure handlers encourage the thinking we want; throwing errors encourages the opposite.

I tend to agree with you.

To that end, I think failure handlers are the right approach. I also think we should make it clear that, once a failure handler is called, there is no saving the process—it is *going* to crash eventually. Maybe failure handlers are `Never`-returning functions, or maybe we simply make it clear that we're going to call `fatalError` after the failure handler runs, but in either case, a failure handler is a point of no return.

(In theory, a failure handler could keep things going by pulling some ridiculous shenanigans, like re-entering the runloop. We could try to prevent that with a time limit on failure handlers, but that seems like overengineering.)

I have a few points of confusion about failure handlers, though:

1. Who sets up a failure handler? The actor that might fail, or the actor which owns that actor?

I imagine it being something set up by the actor’s init method. That way the actor failure behavior is part of the contract the actor provides. Parameters to the init can be used by clients to customize that behavior.

2. Can there be multiple failure handlers?

3. When does a failure handler get invoked? Is it queued like a normal message, or does it run immediately? If the latter, and if it runs in the context of an outside actor, how do we deal with the fact that invariants might not currently hold?

These all need to be defined, I haven’t gone into the design of the API because there are numerous good answers here.

# Distributed actors

I love the feature set you envision here, but I have two major criticisms.

## Heterogeneity is the rule

Swift everywhere is a fine idea, but heterogeneity is the reality. It's the reality today and it will probably be the reality in twenty years. A magic "distributed actor" model isn't going to do us much good if it doesn't work when the actor behind it is implemented in Node, PHP, or Java.

That means that we should expect most distributed actors to be wrappers around marshaling code. Dealing with things like XPC or Neo-Distributed Objects is great, but we also need to think about "distributed actors" based on `JSONEncoder`, `URLSession`, and some custom glue code to stick them together. That's probably most of what we'll end up doing.

I completely agree, we want both.

## It's just a tweaked backend

You describe this as a `distributed` keyword, but I don't think the keyword actually adds much. I don't think there's a simple, binary distinction between distributed and non-distributed actors. Rather, there are a variety of actor "backends"—some in-process, some in-machine, some in-network—which vary in two dimensions:

1. **Is the backend inherently error-prone?** Basically, should actor methods that normally are not `throws` be exposed as `throws` methods because the backend itself is expected to introduce errors in the normal course of operation?

2. **How strictly does the backend constrain the types of parameters you can pass?** In-process, anything that can be safely used by multiple threads is fine. In-machine, it needs to be `Codable` or support `mmap`ing. In-network, it needs to be `Codable`. But that's only the common case, of course! A simple in-machine backend might not support `mmap`; a sophisticated in-network backend might allow you to pass one of your `Actor`s to the other side (where calls would be sent back the other way).

Handling these two dimensions of variation basically requires new protocol features. For the error issue, we basically need typed `throws`, `Never` as a universal subtype (or at least a universal subtype of all `Error`s), and an operation equivalent to `#commonSupertype(BackendError, MethodError)`. For the type-constraining issue, we need an "associated protocol" feature that allows you to constrain `ActorBackend`'s parameters to a protocol specified by the conforming type. And, y'know, a way to reject actor/backend combinations that aren't compatible.

Sure, that’s reasonable. I’m mostly concerned with programmers having an expressive way to describe what they want, and for the language to drag them into doing the right thing when they’ve said they need a capability.

-Chris

···

On Aug 17, 2017, at 11:33 PM, Brent Royal-Gordon <brent@architechies.com> wrote:

On Aug 17, 2017, at 3:24 PM, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

I think we also need `reasync` like `rethrows`.

Sorry, I found it was referred in "rethrows could be generalized to support
potentially async operations".

···

--
Yuta

Hi Chris & swift-evo,

(Given the already lengthy thread I tried to separate my points and keep them reasonably short to allow people to skip points they don't care about. I'm very happy to expand on the points.)

Thanks very much for writing up your thoughts/proposal, I've been waiting to see the official kick-off for the concurrency discussions :).

I) Let's start with the async/await proposal. Personally I think this is the right direction for Swift given the reality that we need to interface with incredibly large existing code-bases and APIs. Further thoughts:

- :question: GCD: dispatching onto calling queue, how?
GCD doesn't actually allow you to dispatch back to the original queue, so I find it unclear how you'd achieve that. IMHO the main reason is that conceptually at a given time you can be on more than one queue (nested q.sync{}/target queues). So which is 'the' current queue?

- ⊄ first class coroutine model => async & throws should be orthogonal
given that the proposal pitches to be the beginning of a first class coroutine model (which I think is great), I think `async` and `throws` do need to be two orthogonal concepts. I wouldn't want automatically throwing generators in the future ;). Also I think we shouldn't throw spanner in the works of people who do like to use Result<E, T> types to hold the errors or values. I'd be fine with async(nothrow) or something though.
- what do we do with functions that invoke their closure multiple times? Like DispatchIO.read/write.

II) the actor model part

- :dancing_women: Erlang runtime and the actor model go hand in hand
I really like the Erlang actor model but I don't think it can be separated from Erlang's runtime. The runtime offers green threads (which allow an actor to block without blocking an OS thread) and prevents you from sharing memory (which makes it possible to kill any actor at any point and still have a reliable system). I don't see these two things happening in Swift. To a lesser extend these issues are also present in Scala/Akka, the mitigate some of the problems by having Akka Streams. Akka Streams are important to establish back-pressure if you have faster producers than consumers. Note that we often can't control the producer, they might be on the other side of a network connection. So it's often very important to not read the available bytes to communicate to the kernel that we can't consumes bytes that fast. If we're networking with TCP the kernel can then use the TCP flow-control to signal to the other side that they better slow down (or else packets will be dropped and then need to be resent later).

- :boom: regarding fatal failure in actors
in the server world we need to be able to accept hundreds of thousands (millions) of connections at the same time. There are quite a few cases where these connections are long-lived and paused for most of the the time. So I don't really see the value in introducing a 'reliable' actor model where the system stops accepting new connections if one actor fatalError'd and then 'just' finishes up serving the existing connections. So I believe there are only two possible routes: 1) treat it like C/C++ and make sure your code doesn't fatalError or the whole process blows up (what we have right now) 2) treat it like Erlang and let things die. IMHO Erlang wouldn't be successful if actors couldn't just die or couldn't be linked. Linking propagates failures to all linked processes. A common thing to do is to 1) spawn a new actor 2) link yourself to the newly spawned actor 3) send a message to that actor and at some point eventually await a reply message sent by the actor spawned earlier. As you mentioned in the writeup it is a problem if the actor doesn't actually reply which is why in Erlang you'd link them. The effect is that if the actor we spawned dies, any linked actor will die too which will the propagate the error to an appropriate place. That allows the programmer to control where an error should propagate too. I realise I'm doing a poor job in explaining what is best explained by documentation around Erlang: supervision [1] and the relationship between what Erlang calls a process (read 'actor') and errors [2].

- :hotsprings: OS threads and actors
as you mention, the actor model only really works if you can spawn lots of them, so it's very important to be able to run hundreds of thousands of them on a number of OS threads pretty much equal to your number of cores. That's only straightforward if there are no (OS thread) blocking operations or at least it's really obvious what blocks and what doesn't. And that's not the case in Swift today and with GCD you really feel that pain. GCD does spawn threads for you and has a rather arbitrary limit of 64 OS threads (by default on macOS). That is too many for a very scalable server application but too few to just tolerate blocking APIs.

[1]: http://erlang.org/documentation/doc-4.9.1/doc/design_principles/sup_princ.html
[2]: Errors and Processes | Learn You Some Erlang for Great Good!

-- Johannes

···

On 17 Aug 2017, at 11:25 pm, Chris Lattner via swift-evolution <swift-evolution@swift.org> wrote:

On Aug 17, 2017, at 3:24 PM, Chris Lattner <clattner@nondot.org> wrote:

Hi all,

As Ted mentioned in his email, it is great to finally kick off discussions for what concurrency should look like in Swift. This will surely be an epic multi-year journey, but it is more important to find the right design than to get there fast.

I’ve been advocating for a specific model involving async/await and actors for many years now. Handwaving only goes so far, so some folks asked me to write them down to make the discussion more helpful and concrete. While I hope these ideas help push the discussion on concurrency forward, this isn’t in any way meant to cut off other directions: in fact I hope it helps give proponents of other designs a model to follow: a discussion giving extensive rationale, combined with the long term story arc to show that the features fit together.

Anyway, here is the document, I hope it is useful, and I’d love to hear comments and suggestions for improvement:
Swift Concurrency Manifesto · GitHub

Oh, also, one relatively short term piece of this model is a proposal for adding an async/await model to Swift (in the form of general coroutine support). Joe Groff and I wrote up a proposal for this, here:
Concrete proposal for async semantics in Swift · GitHub

and I have a PR with the first half of the implementation here:
Async await prototype by lattner · Pull Request #11501 · apple/swift · GitHub

The piece that is missing is code generation support.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I'm also available to help on the topic of pure functions, if you need any :)

Elviro

···

I suggested a little while ago on this list some principles based on this that would allow for the compiler to enforce value semantics with a `pure` attribute and I'm currently working on a draft proposal. In essence this proposal will have pure functions guaranteeing value semantics and no access to the global state, and a correct implementation for copy-on-write types. I think this would be useful for actors.

--
Michel Fortin
https://michelf.ca <https://michelf.ca/&gt;

I agree. If we are going to keep “async” and “throws” as unrelated axes on a matrix, then there cannot be a relationship implied by the ordering. “throws async” and “async throws” need to mean the same thing.

-Chris

···

On Aug 18, 2017, at 6:04 PM, Yuta Koshizawa <koher@koherent.org> wrote:

Hi, I have a question about the proposed `async/await`.

Are `throws async` and `await try` allowed? I think we have three options.

1. allows only `async throws`-`try await`
2. allows both `async throws`-`try await` and `throws async`-`await try` and does not distinguish them
3. allows both and distinguishes them like `Promise<Result<T>>` and `Result<Promise<T>>`

Although 3 is the most expressive, I think it is too complicated. In most cases we need only something similar to `Promise<Result<T>>`. To select 1 also makes it possible to support 3 in the future. So 1 seems a good choice to me.

2017/08/19 3:51、Chris Lattner <clattner@nondot.org>ăźăƒĄăƒŒăƒ«:

Part 1: Async/await

    let dataResource = await loadWebResource("dataprofile.txt")

Was there any thought about introducing the concept of a timeout when awaiting?

Something like an `await for:` optional parameter?
Then, if used with try, it could go like


    let dataResource = try await for:10/*seconds*/ loadWebResource("dataprofile.txt”) catch _ as Timeout { //abort,retry cancel }

Timeouts should probably be handled at a higher level, but its just something that jumped out to me a little, since its something I notice sometimes people neglect to take care of
 :/

Something like this could definitely be built and added later: the sketch for async/await is intentionally kept as minimal as possible. That said, this seems like it is wandering into territory that would make sense to be API, rather than first class language feature.

I see, yea, the only reason I brought it up is because it jumped out as an important concept in my mind that a lot of times seems to get neglected when people write async operations (e.g “how long am I willing to wait for this to finish”.... there are a lot of times I have to force quit apps that’s freeze or become non-responsive, and it’s almost alway waiting to acquire some resource) so i just felt it would be good to have upfront support in some manner for timeouts...

Part 2: Actors
One other thing that also jumped at me was, if we are going to have actors, and having them encapsulate/isolate state, would it also make sense to make sure that we can’t invoke state changing messages when its invalid?

As an example, if we had a ”downloader” actor that downloads multiple files, we wouldn't want to be able to send invalid messages such as `begin` when downloading has already ”begun”
. Would it then make more sense to have callable messages be determined by the publicly visible state of the actor instead?

Yes, I completely agree and know what you’re talking about here. You’re unintentionally poking one of my other axes the grind. :)

Wow great! ^^

This desire (to have conceptual “states” that a type moves through) is something that is super common. Files can be in “initialized” and “open” states, and that changes the valid actions that can be performed on them. A mutex can be locked or unlocked etc. Being able to express these invariants is highly related to pre and post conditions.

One abstraction that has been developed in the industry is the notion of typestate (https://en.wikipedia.org/wiki/Typestate_analysis\), which underlies common abstractions in static analyzer tools like the Clang Static Analyzer. I would love to see these ideas developed and built out at some point in time, and perhaps the next time I get a long block of time I’ll write another manifesto on those ideas ;-)

I would very very much look forwards to that manifesto and would love to help out in any way in supporting such a concept!

Coming back to your point, these ideas apply just as much to structs and classes as they do actors, so I think it should be a feature orthogonal to actors.

Definitely true, that.

···

On Aug 17, 2017, at 10:50 PM, Andre <pyunpyun@me.com> wrote:

—

If this was to be a feature at some point, when do you foresee a possible inclusion for it?
Swift 5,or 6 timeframe?

Meanwhile I will go and start playing with the languages (plaid etc) in the typestate theory wiki link you just shared! I’m glad there is a formal theory behind this, very cool.

Andre

iPadから送信

-Chris

Also, notice the consistency

keyword: throw, return type marker: throws (‘monadic’ Result<T>)
keyword: yield, return type marker: yields (‘monadic’ Future<T>)

-g.

···

On 19 Aug 2017, at 1:23 PM, Georgios Moschovitis via swift-evolution <swift-evolution@swift.org> wrote:

I am wondering, am I the only one that *strongly* prefers `yield` over `await`?

Superficially, `await` seems like the standard term, but given the fact that the proposal is about coroutines, I think `yield` is actually the proper name. Also, subjectively, it sounds much better/elegant to me!

-g.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Swift tends to take a pragmatic view of this kind of thing, naming features after their common uses rather than their formal names. For instance, there's no technical reason you *have* to use the error-handling features for errors—you could use them for routine but "special" return values like breaking out of a loop—but we still name things like the `Error` protocol and the `try` keyword in ways that emphasize their use for errors.

This feature is about coroutines, sure, but it's a coroutine feature strongly skewed towards use for asynchronous calls, so we prefer syntax that emphasizes its async-ness. When you're reading the code, the fact that you're calling a coroutine is not important; what's important is that the code may pause for a while during a given expression and run other stuff in the meantime. `await` says that more clearly than `yield` would.

···

On Aug 19, 2017, at 3:23 AM, Georgios Moschovitis via swift-evolution <swift-evolution@swift.org> wrote:

I am wondering, am I the only one that *strongly* prefers `yield` over `await`?

Superficially, `await` seems like the standard term, but given the fact that the proposal is about coroutines, I think `yield` is actually the proper name. Also, subjectively, it sounds much better/elegant to me!

--
Brent Royal-Gordon
Architechies

Re: <https://gist.github.com/lattner/429b9070918248274f25b714dcfc7619&gt;

Chris Lattner recently commented in <https://github.com/apple/swift/pull/11501&gt; that the prototype could use <https://llvm.org/docs/Coroutines.html&gt; support.

In one of the CppCon videos, Gor Nishanov said that C++ coroutines won't have an `async` keyword, and will be compatible with function pointers in C and C++.

* <https://channel9.msdn.com/Events/CPP/C-PP-Con-2014/0006-await-20-Stackless-Resumable-Functions&gt;

* <https://channel9.msdn.com/events/CPP/CppCon-2015/CPPConD03V008&gt;

* <https://channel9.msdn.com/events/CPP/CppCon-2016/CppCon-2016-Gor-Nishanov-C-Coroutines-Under-the-covers&gt;

I couldn't find the reason for this decision; does anyone here know why C++ coroutines don't need an `async` keyword?

And/or why do Swift coroutines need the `async` keyword? Does it imply a hidden parameter, like the `throws` keyword?

-- Ben

Cancellation and time out can be built into futures, and async/await can interact with futures. I don’t think we need async/await itself to support either of those.

Just as a real-world example, C#’s async/await feature doesn’t have built-in timeout or cancellation support, but it’s still easy to handle both of those cases using the tools available. For example, one technique would be this (in C#):

var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMilliseconds(2500));
try {
  await DoAsync(cts.Token);
}
catch (OperationCanceledException) {
  // Handle cancelled
}
catch (Exception) {
  // Handle other failure
}

There are other techniques that would let you distinguish between cancellation and timeout as well.

···

On Aug 25, 2017, at 7:06 AM, Cavelle Benjamin via swift-evolution <swift-evolution@swift.org> wrote:

Disclaimer: not an expert

Question
I didn’t see any where the async is required to time out after a certain time frame. I would think that we would want to specify both on the function declaration side as a default and on the function call side as a customization. That being said, the return time then becomes an optional given the timeout and the calling code would need to unwrap.

func loadWebResource(_ path: String) async -> Resource
func decodeImage(_ r1: Resource, _ r2: Resource) async -> Image
func dewarpAndCleanupImage(_ i : Image) async -> Image

func processImageData1() async -> Image {
    let dataResource = await loadWebResource("dataprofile.txt")
    let imageResource = await loadWebResource("imagedata.dat")
    let imageTmp = await decodeImage(dataResource, imageResource)
    let imageResult = await dewarpAndCleanupImage(imageTmp)
    return imageResult
}

So the prior code becomes


func loadWebResource(_ path: String) async(timeout: 1000) -> Resource?
func decodeImage(_ r1: Resource, _ r2: Resource) async -> Image?
func dewarpAndCleanupImage(_ i : Image) async -> Image?

func processImageData1() async -> Image? {
    let dataResource = guard let await loadWebResource("dataprofile.txt”) else { // handle timeout }
    let imageResource = guard let await(timeout: 100) loadWebResource("imagedata.dat”) else { // handle timeout }
    let imageTmp = await decodeImage(dataResource, imageResource)
    let imageResult = await dewarpAndCleanupImage(imageTmp)
    return imageResult
}

Given this structure, the return type of all async’s would be optionals with now 3 return types??

.continuation // suspends and picks back up
.value // these are the values we are looking for
.none // took too long, so you get nothing.

On 2017-Aug -17 (34), at 18:24, Chris Lattner via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Hi all,

As Ted mentioned in his email, it is great to finally kick off discussions for what concurrency should look like in Swift. This will surely be an epic multi-year journey, but it is more important to find the right design than to get there fast.

I’ve been advocating for a specific model involving async/await and actors for many years now. Handwaving only goes so far, so some folks asked me to write them down to make the discussion more helpful and concrete. While I hope these ideas help push the discussion on concurrency forward, this isn’t in any way meant to cut off other directions: in fact I hope it helps give proponents of other designs a model to follow: a discussion giving extensive rationale, combined with the long term story arc to show that the features fit together.

Anyway, here is the document, I hope it is useful, and I’d love to hear comments and suggestions for improvement:
Swift Concurrency Manifesto · GitHub

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I think we would be better off with a future type rather than async/await
since they can offer timeout, cancel, and control over which thread
execution occurs on.

  -- Howard.

···

On 26 August 2017 at 00:06, Cavelle Benjamin via swift-evolution < swift-evolution@swift.org> wrote:

Disclaimer: not an expert

Question
I didn’t see any where the async is required to time out after a certain
time frame. I would think that we would want to specify both on the
function declaration side as a default and on the function call side as a
customization. That being said, the return time then becomes an optional
given the timeout and the calling code would need to unwrap.

func loadWebResource(_ path: String) async -> Resourcefunc decodeImage(_ r1: Resource, _ r2: Resource) async -> Imagefunc dewarpAndCleanupImage(_ i : Image) async -> Image
func processImageData1() async -> Image {
    let dataResource = await loadWebResource("dataprofile.txt")
    let imageResource = await loadWebResource("imagedata.dat")
    let imageTmp = await decodeImage(dataResource, imageResource)
    let imageResult = await dewarpAndCleanupImage(imageTmp)
    return imageResult
}

So the prior code becomes


func loadWebResource(_ path: String) async(timeout: 1000) -> Resource?func decodeImage(_ r1: Resource, _ r2: Resource) async -> Image?func dewarpAndCleanupImage(_ i : Image) async -> Image?
func processImageData1() async -> Image? {
    let dataResource = guard let await loadWebResource("dataprofile.txt”) else { // handle timeout }
    let imageResource = guard let await(timeout: 100) loadWebResource("imagedata.dat”) else { // handle timeout }
    let imageTmp = await decodeImage(dataResource, imageResource)
    let imageResult = await dewarpAndCleanupImage(imageTmp)
    return imageResult
}

Given this structure, the return type of all async’s would be optionals
with now 3 return types??

.continuation // suspends and picks back up
.value // these are the values we are looking for
.none // took too long, so you get nothing.

On 2017-Aug -17 (34), at 18:24, Chris Lattner via swift-evolution < > swift-evolution@swift.org> wrote:

Hi all,

As Ted mentioned in his email, it is great to finally kick off discussions
for what concurrency should look like in Swift. This will surely be an
epic multi-year journey, but it is more important to find the right design
than to get there fast.

I’ve been advocating for a specific model involving async/await and actors
for many years now. Handwaving only goes so far, so some folks asked me to
write them down to make the discussion more helpful and concrete. While I
hope these ideas help push the discussion on concurrency forward, this
isn’t in any way meant to cut off other directions: in fact I hope it helps
give proponents of other designs a model to follow: a discussion giving
extensive rationale, combined with the long term story arc to show that the
features fit together.

Anyway, here is the document, I hope it is useful, and I’d love to hear
comments and suggestions for improvement:
Swift Concurrency Manifesto · GitHub

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hi All.

A point of clarification in this example:

func loadWebResource(_ path: String) async -> Resource
func decodeImage(_ r1: Resource, _ r2: Resource) async -> Image
func dewarpAndCleanupImage(_ i : Image) async -> Image

func processImageData1() async -> Image {
    let dataResource = await loadWebResource("dataprofile.txt")
    let imageResource = await loadWebResource("imagedata.dat")
    let imageTmp = await decodeImage(dataResource, imageResource)
    let imageResult = await dewarpAndCleanupImage(imageTmp)
    return imageResult
}

Do these:

await loadWebResource("dataprofile.txt")
await loadWebResource("imagedata.dat")

happen in in parallel?

They don’t happen in parallel.

···

On 8 Sep 2017, at 20:34, Kenny Leung via swift-evolution <swift-evolution@swift.org> wrote:

If so, how can I make the second one wait on the first one? If not, how can I make them go in parallel?

Thanks!

-Kenny

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution