# \[Pitch #2\] Async/await

**URL:** <https://forums.swift.org/t/pitch-2-async-await/42420>\
**Category:** Pitches\
**Created:** [December 2, 2020, 12:04am UTC](https://forums.swift.org/t/pitch-2-async-await/42420 "2020-12-02T00:04:54Z")\
**Posts on this page:** 14\
**Page:** 3

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [December 6, 2020, 2:29pm UTC](https://forums.swift.org/t/pitch-2-async-await/42420/41 "2020-12-06T14:29:08Z")

</div>

> The operand of an `await` expression must contain at least one potential suspension point, although more than one potential suspension point is allowed within an `await` 's operand.

This sounds stricter than the current `try`. You can use `try` without `throws` and get away relatively scot-free with just a warning. Is this intentional, or is it just the wording?

---

<div class="post-metadata">

**Author:** ![jayton](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jayton/32/5592_2.png) [@jayton](https://forums.swift.org/u/jayton)\
**Post date:** [December 6, 2020, 3:00pm UTC](https://forums.swift.org/t/pitch-2-async-await/42420/42 "2020-12-06T15:00:34Z")

</div>

> [@Karl](#):
>
> Interleaving concerns are more of an actor thing than an async/await thing, because the model of writing instance methods on a type gives you similar expectations about instance variables as you get for local variables

The pitch allows for (and demonstrates) `async` methods on non-actor types including classes, so interleaving issues with instance state apply there too. I expect that bugs caused by interleaving on global state will be relatively common too, although probably less so than with instance state.

---

<div class="post-metadata">

**Author:** ![michelf](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/michelf/32/1180_2.png) [@michelf](https://forums.swift.org/u/michelf)\
**Post date:** [December 6, 2020, 3:54pm UTC](https://forums.swift.org/t/pitch-2-async-await/42420/43 "2020-12-06T15:54:02Z")

</div>

> [@Karl](#):
>
> If potential suspension points within an actor were louder and more explicit, like completion handler-driven code, I expect there would be less concern about interleaving. Maybe it’s just something people will need time to get used to.

Having louder suspension points would lessen the concern that interleaving will slip unnoticed. But `await` is loud enough for me. What isn't loud enough is the warning when introducing the concept. At the very least, the proposal should have an example of something where interleaving can be problematic. Currently, this is only discussed in the abstract and easily dismissed.

The proposal says to be careful with mutexes and blocking functions, but it offers no alternative. It says: "Ongoing library work to provide abstractions that allow programs to avoid these pitfalls will be required." It's vague and subtle though. I think the proposal should expand a bit on that, perhaps in Future Directions, so people can have some idea of how this is expected to work in an `async` world.

---

<div class="post-metadata">

**Author:** ![Chris\_Lattner3](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/chris_lattner3/32/348_2.png) [@Chris\_Lattner3](https://forums.swift.org/u/Chris_Lattner3)\
**Post date:** [December 6, 2020, 10:06pm UTC](https://forums.swift.org/t/pitch-2-async-await/42420/44 "2020-12-06T22:06:59Z")

</div>

> [@Douglas\_Gregor](#):
>
> Top-level code is not an asynchronous context according to this proposal, so no, you cannot currently do this. I'll clarify the proposal text in this regard.

This sounds like a fine starting point, but I think it is worth making TLC in scripts and playgrounds be implicitly async-compatible, just like it is implicitly throw-compatible. Among other things, this will make it _much_ easier to teach people async code, and will make scripts much less annoying in an async filled world 🙂. This can obviously be a follow-on proposal after the major details are settled.

-Chris

---

<div class="post-metadata">

**Author:** ![Chris\_Lattner3](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/chris_lattner3/32/348_2.png) [@Chris\_Lattner3](https://forums.swift.org/u/Chris_Lattner3)\
**Post date:** [December 6, 2020, 10:45pm UTC](https://forums.swift.org/t/pitch-2-async-await/42420/45 "2020-12-06T22:45:57Z")

</div>

> [@Douglas\_Gregor](#):
>
> There isn't a difference between async actor methods and normal async methods. Both can have `inout` parameters. The restrictions we need are on which arguments you can provide, to ensure that you have exclusive access to the argument to the `inout` even across a suspension. The [actor proposal](https://github.com/DougGregor/swift-evolution/blob/actors/proposals/nnnn-actors.md#inout-parameters) states the restriction for actor-isolated instance members:
> 
> > Actor-isolated stored properties can be passed into synchronous functions via inout parameters, but it is ill-formed to pass them to asynchronous functions via inout parameters. For example:  
> > ...  
> > This restriction prevents exclusivity violations where the modification of the actor-isolated balance is initiated by passing it as inout to a call that is then suspended, and another task executed on the same actor then fails with an exclusivity violation in trying to access balance itself.
> 
> It doesn't matter whether `modifiesAsynchronously` is on an actor or not; you still can't pass `&self.balance` to it.
> 
> However, it would be fine to pass `&myLocalVariable` to it so long as we've already ensured that `myLocalVariable` doesn't have captures that can run concurrently. That's partially implemented as a warning in the experimental concurrency mode, and I promised (somewhere these threads) to bring that into one of the proposals... but haven't done so yet.

Thank you for the clarification, I think I understand what you're going for here: you don't want actor isolation to be broken by `inout` parameters. I've been trying to really grok how this works with the proposed model, but I don't think I've really succeeded.

There are two different but confusingly similar concerns that I'm trying to come to grips with:

1. Actor isolation doesn't want references to state to be transferred across actor boundaries in a way that can introduce race conditions / unsafety. Getting this right is a key part of the actor model design, related to the global semantic discussions, actor sendable, etc.

2. The "reentrancy" aspect of actors combined with the "opening of accesses in `inout` parameters" means that things passed as inout parameters can have their accessed opened for "a long time" and cause unexpected exclusivity failures that turn into dynamic crashes that may be difficult to test. This can cause unpredictable and difficult to debug crashes in the field.

If I understand correctly you're trying to solve both problems at the same type by preventing forming inout argument references to properties of actors, e.g. limiting inout binding to local variables.

However, I'm not sure (said differently: every time I think about this I confuse myself :) that this is either necessary or sufficient. Here's my uncertainty:

- Insufficient: Local variables are part of the actor protected state. They can escape locally in the actor when captured by-ref in a closure. Passing it as an inout argument can cause a race condition between the actors thread/queue/context and the receiving actor's thread. I suppose this would get caught dynamically as an exclusive access violation, but it provokes the second issue above.

- Necessary: actors can have non-actor crossing sync and async methods **within** their concurrency domain. It should be perfectly fine to pass actor properties as `inout` parameters to these intra-actor functions. For example, my understanding of your rule is that no call to a mutating async method on an actor property would be allowed.

* * *

I'm primarily concerned about the cross-actor usage of inout parameters from a memory safety perspective. I don't see any obvious way to make this work well, because `inout` intentionally introducing aliases of actor-protected state in the cross-actor condition.

However, I think the secondary issue is a pretty significant one in practice as well. Even if dynamic exclusivity checks catch the problem if/when the problem manifests in practice, these crashes will be rare, require specific concurrency patterns to manifest, and therefore lead to bugs in practice.

This also doesn't seem like an important case to support for expressivity, so I'd prefer to define away the foot gun and complexity entirely.

* * *

Concrete proposal / recommendation / thought:

The compiler already has to reason about cross-actor calls - this is where sync methods become async (requiring await), and the `ActorSendable` sorts of checks have to be performed. Would it be reasonable to just forbid passing anything `inout` across these actor boundaries?

This fixes the unnecessary limitation of intra-actor references, and such a limitation could be relaxed in the future if there is a safe model for it. It would also eliminate a complicated part of the actor model we'd otherwise have to nail down.

-Chris

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [December 7, 2020, 12:02am UTC](https://forums.swift.org/t/pitch-2-async-await/42420/46 "2020-12-07T00:02:42Z")

</div>

> [@Chris\_Lattner3](#):
>
> Local variables are part of the actor protected state.

I think the description makes a lot more sense if the local variables are _not_ protected. Whether they should, or if we can reign them in is another story.

Actually, I don't see how `async inout` works with protected states (by essentially any definition) at all.

> [@Chris\_Lattner3](#):
>
> They can escape locally in the actor when captured by-ref in a closure.

Which scenarios are you thinking about? You normally can't escape an `inout` reference. You need to make a copy in one way or another.

> [@Chris\_Lattner3](#):
>
> For example, my understanding of your rule is that no call to a mutating async method on an actor property would be allowed.

I don't see how that would work. While the method is suspending, accessing that property is totally undefined, and we _need_ to be able to access it during the interleaves.

> [@Chris\_Lattner3](#):
>
> The "reentrancy" aspect of actors combined with the "opening of accesses in `inout` parameters" means that things passed as inout parameters can have their accessed opened for "a long time" and cause unexpected exclusivity failures that turn into dynamic crashes that may be difficult to test.

That is quite a problem 🤔🤔. Though it's about as much as escaping the same variable in two places, which is about as much as I'd expect.

* * *

I did mention it on the other thread but I have an inkling that, if we can figure out what variables can go in escaping (sync) closure, we could simultaneously solve the async inout problem.

---

<div class="post-metadata">

**Author:** ![Chris\_Lattner3](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/chris_lattner3/32/348_2.png) [@Chris\_Lattner3](https://forums.swift.org/u/Chris_Lattner3)\
**Post date:** [December 7, 2020, 1:27am UTC](https://forums.swift.org/t/pitch-2-async-await/42420/47 "2020-12-07T01:27:06Z")

</div>

> [@Lantua](#):
>
> > [@Chris\_Lattner3](#):
> >
> > They can escape locally in the actor when captured by-ref in a closure.
> 
> Which scenarios are you thinking about? You normally can't escape an `inout` reference. You need to make a copy in one way or another.

I'm thinking about this case:

```swift
actor Foo {
  var otherActor : OtherActor

  func someActorMethod() async {
     var x = 42
     doThingWithEscapingClosure { x = 57 }

     otherActor.crossActorCall(&x)
  }
}

```

In this case, `x` is a normal local variable, but "doThingWithEscapingClosure" can be calling the closure as often as it wants, mutating it in the background even when `otherActor` is touching it.

> [@Lantua](#):
>
> > [@Chris\_Lattner3](#):
> >
> > For example, my understanding of your rule is that no call to a mutating async method on an actor property would be allowed.
> 
> I don't see how that would work. While the method is suspending, accessing that property is totally undefined, and we _need_ to be able to access it during the interleaves.

That's my point, it seems like it should work. Consider these examples that have nothing to do with actors:

```swift
  extension Bool {
     mutating func asyncTwiddle() async { ... }
  }
  func otherAsyncThing(x : inout Bool) async { ... }

```

it seems like this should be allowed:

```swift
actor Foo {
   var x : Bool

   func internalImplDetail() async {
      await x.asyncTwiddle()
      await otherAsyncThing(&x)
   }
}

```

But it seems to be banned by this proposal because x is being passed `inout` in both cases, even though this is a purely intra-actor computation.

-Chris

---

<div class="post-metadata">

**Author:** ![Lantua](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lantua/32/8868_2.png) [@Lantua](https://forums.swift.org/u/Lantua)\
**Post date:** [December 7, 2020, 1:50am UTC](https://forums.swift.org/t/pitch-2-async-await/42420/48 "2020-12-07T01:50:30Z")

</div>

> [@Chris\_Lattner3](#):
>
> it seems like this should be allowed:

That shouldn't work. There's this case:

```swift
extension Bool {
  mutating func asyncTwiddle() async {
    // 2
    await ... // suspend
  }
}

actor Foo {
  var x: Bool
  func internalImplDetail() async {
    // 1
    await x.asyncTwiddle()
  }
}

```

If we have:

```swift
// In actor A
await someFoo.internalImplDetail()

// In actor B
await someFoo.internalImplDetail()

```

we have a problem when

1. `A` enters (1), (2)
2. `A` suspends
3. `B` enters (1)
4. `B` _cannot_ enter (2) because of exclusivity

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [December 7, 2020, 6:40am UTC](https://forums.swift.org/t/pitch-2-async-await/42420/49 "2020-12-07T06:40:38Z")

</div>

I expect that async `mutating` functions will not be supported, for the same reason that we can’t support async property setters or subscripts.

> Special functions like `deinit` and storage accessors (i.e., the getters and setters for properties and subscripts) cannot be `async` .
> 
> > **Rationale** : Properties and subscripts that only have a getter could potentially be `async` . However, properties and subscripts that also have an `async` setter imply the ability to pass the reference as `inout` and drill down into the properties of that property itself, which depends on the setter effectively being an "instantaneous" (synchronous, non-throwing) operation. Prohibiting `async` properties is a simpler rule than only allowing get-only `async` properties and subscripts.

Since this is a permanent “can’t fix” rather than a temporary limitation, I’d rather that we did support get-only async properties and subscripts, and changed the rule to forbid async setters or `mutating` methods.

It’s already hard enough to decide between writing a function or read-only computed property. Please don’t break the flimsy “rules of thumb” that we’ve somehow landed on 😅

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [December 7, 2020, 9:22pm UTC](https://forums.swift.org/t/pitch-2-async-await/42420/50 "2020-12-07T21:22:02Z")

</div>

> [@jayton](#):
>
> > [async functions] only give up their thread when they reach what’s called a suspension point, marked by `await` .
> 
> is the first mention of suspension points in the text, and I feel it’s most easily understood as implying that async functions yield time on _every_ await. Since it’s the introduction of the concept, that potentially primes the reader to misunderstand (especially if they skim over things they believe they already understand).

Okay, I can understand the confusion there. I think mentioning `await` at all here is causing confusion.

> [@jayton](#):
>
> It seems to me that quite a bit of confusion and criticism of the concurrency pitches hinges on this misunderstanding, so it’s probably worth wordsmithing a bit rather than just inserting “can”. Perhaps:
> 
> > In fact, asynchronous functions never just spontaneously give up their thread; the only time this can happen is when the function reaches a _potential suspension point_ , marked by `await` .
> 
> However, the rest of the paragraph, and the following section called **Suspension points** , also fail to make the distinction clear. Overall, the “Proposed solution” section doesn’t adequately describe the solution you actually intend to implement.

I'm attempting to address your concerns with [[SE-0296] Try to clarify potential suspension points by DougGregor · Pull Request #1225 · apple/swift-evolution · GitHub](https://github.com/apple/swift-evolution/pull/1225), to try to make it clear that we are marking _potential_ suspension points with `await` and giving some reasons why one might not known whether a potential suspension point is, in fact, an actual suspension point.

> [@jayton](#):
>
> > [@Karl](#):
> >
> > Interleaving concerns are more of an actor thing than an async/await thing, because the model of writing instance methods on a type gives you similar expectations about instance variables as you get for local variables
> 
> The pitch allows for (and demonstrates) `async` methods on non-actor types including classes, so interleaving issues with instance state apply there too. I expect that bugs caused by interleaving on global state will be relatively common too, although probably less so than with instance state.

To Karl's point, the async/await proposal is neither trying to handle data races nor trying to describe actors. Suspension points moving a task to a different thread breaks some atomicity assumptions; whether actors do that by default or not, and what kind of facilities help prevent accidental data races here, are intentionally left to the other, later proposals. This document shouldn't try to describe those in detail.

> [@michelf](#):
>
> The proposal says to be careful with mutexes and blocking functions, but it offers no alternative. It says: "Ongoing library work to provide abstractions that allow programs to avoid these pitfalls will be required." It's vague and subtle though. I think the proposal should expand a bit on that, perhaps in Future Directions, so people can have some idea of how this is expected to work in an `async` world.

I think this is far enough out of scope for the async/await proposal that we shouldn't try to put it into Future Directions at all (and I've removed the "Ongoing library work..." sentence that hints at it). We might be able to provide some power-user APIs (say, as part of the Task library) to cope with them, but we shouldn't guess at what they would look like, and it's more important for this proposal to stay focused.

Doug

---

<div class="post-metadata">

**Author:** ![Douglas\_Gregor](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/douglas_gregor/32/1362_2.png) [@Douglas\_Gregor](https://forums.swift.org/u/Douglas_Gregor)\
**Post date:** [December 7, 2020, 9:27pm UTC](https://forums.swift.org/t/pitch-2-async-await/42420/51 "2020-12-07T21:27:21Z")

</div>

> [@Lantua](#):
>
> > The operand of an `await` expression must contain at least one potential suspension point, although more than one potential suspension point is allowed within an `await` 's operand.
> 
> This sounds stricter than the current `try` . You can use `try` without `throws` and get away relatively scot-free with just a warning. Is this intentional, or is it just the wording?

This is unnecessarily stricter than `try`, for no reason. And the implementation was emitting a warning anyway. I'll update the proposal to match the implementation and `try`.

Doug

---

<div class="post-metadata">

**Author:** ![Chris\_Lattner3](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/chris_lattner3/32/348_2.png) [@Chris\_Lattner3](https://forums.swift.org/u/Chris_Lattner3)\
**Post date:** [December 10, 2020, 6:17pm UTC](https://forums.swift.org/t/pitch-2-async-await/42420/52 "2020-12-10T18:17:37Z")

</div>

> [@Lantua](#):
>
> If we have:
> 
> ```swift
> // In actor A
> await someFoo.internalImplDetail()
> 
> // In actor B
> await someFoo.internalImplDetail()
> 
> ```
> 
> we have a problem when
> 
> 1. `A` enters (1), (2)
> 2. `A` suspends
> 3. `B` enters (1)
> 4. `B` _cannot_ enter (2) because of exclusivity

It's not a "problem" in the memory safety sense, but it is a footgun. This is why I was/am concerned about getting the interaction between actors and `inout` exclusivity right. That said, this is an actor level concern, not an async/await concern, so it seems best to move it to that level of the stack when it comes up.

---

<div class="post-metadata">

**Author:** ![ktoso](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ktoso/32/44526_2.png) [@ktoso](https://forums.swift.org/u/ktoso)\
**Post date:** [March 3, 2021, 8:13am UTC](https://forums.swift.org/t/pitch-2-async-await/42420/55 "2021-03-03T08:13:59Z")

</div>

@lihansey please note that you are commenting and reading an outdated thread.

The proposal has been updated multiple times since the Pitch #2, and here is the latest version which was accepted: [[Accepted with Modification] SE-0296: async/await](https://forums.swift.org/t/accepted-with-modification-se-0296-async-await/43318)

May be worth closing this thread?

---

<div class="post-metadata">

**Author:** ![John\_McCall](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/john_mccall/32/1351_2.png) [@John\_McCall](https://forums.swift.org/u/John_McCall)\
**Post date:** [March 3, 2021, 8:34am UTC](https://forums.swift.org/t/pitch-2-async-await/42420/57 "2021-03-03T08:34:19Z")

</div>



[Previous page](https://forums.swift.org/t/pitch-2-async-await/42420.md?page=2)
