Suspension guarantees when isolation does not change

I have a question about suspension. SE-0420 says this:

Avoiding extra suspensions from actor-isolated code can also be semantically important because code from other tasks can interleave on the actor during suspensions, potentially changing the values stored in isolated storage; this is guaranteed not to happen at the moments of call and return between functions with the same isolation.

Does this mean the transition from points A to B in this example is guaranteed to be synchronous? Is that true in all cases when the isolation does not change?

actor MyActor {
  func funcA() async {
    // point A
    await funcB()
  }
    
  func funcB() async {
    // point B
  }
}
3 Likes

Yeah, this is something you can rely on because of the "await is only marking potential suspension points" and this code actually has nothing that'd force a suspension...

Since you're "on" the actor already, and not "giving it up", the funcB call should not have a way to fail "getting on the actor".

At least today this is guaranteed and code does rely on it frequently.

5 Likes

Should the compiler diagnose a warning about an unnecessary await here, to reinforce the guarantee that an actor-isolated function that calls another function that is isolated to the same actor will not suspend?

funcB here is declared async so I think it's appropriate to require await. With detailed knowledge of the behavior of funcB one might be able to 'know' that funcB won't actually suspend, but funcB has declared its intent that it should be treated as though it's going to suspend. (And indeed, under Swift's current rules the await is not unnecessary—it is an error to omit the await here.)

4 Likes

Ah, yes. I had a feeling I was missing something.

This sentence is giving me pause. I also want to rely on this, but it kinda sounds like one day this might change.

I just want to be really clear because this is a pretty powerful assumption, and should it no longer be synchronous, my code would completely break.

In my example, funcB does nothing. So, I see why the question was being asked.

But, a non-contrived example could peform out-of-isolation operations that would then necessitate suspension. Given this, isn't the await is absolutely necessary given these function signatures?

It's more about "well you COULD–accidentally or on purpose–make funcB suspend for real and then you'd get interleaving potential".

3 Likes

My question is about the mechnics of the function call itself. Even if funcB does cause a suspension, the transition from point A to point B should remain synchronous, shouldn't it?

1 Like

It should, yeah.

2 Likes

What I want to say is something like:

A async function call that does not change isolation is guaranteed to transfer control into the callee synchronously. But, the callee could then itself make an async call that results in suspension.

Does this sound right?

1 Like

At least today that's true, yes. I don't expect this to change tbh.

1 Like

It is intended to be a guarantee that async function calls between functions with the same isolation will not suspend just as part of performing the call or returning from the call. The only gap I know of in that guarantee involves calls through Objective-C async (i.e. completion-handler-based) interfaces.

9 Likes

Answers the question perfectly. Thank you so much!

1 Like