Await closure and isolation

I like to analyze tests in /test/Concurency/
In the process of writing the code, I had a few questions.
I created my global actor PrettyActor with the myAssertation func.

First question:
Is await {...}() closure equivalent to unstructured Task {...} ? Does @_inheritActorContext have an attribute with this closure like Task ?

@globalActor
actor PrettyActor {
	static var shared = PrettyActor()
	
	@PrettyActor
	static func myAssertation() async {
		await {
			Self.assertIsolated()
		}()
	}
}

Second question:
In code below why does the myAssertation() method pass the .assertIsolated() check if we call it in MainActor Task { @MainActor in … } ?

Task { @MainActor in
	await PrettyActor.myAssertation()
}

No. It's calling an async closure, that's all. So it's Structured Concurrency.

Are you not getting a warning here?

No 'async' operations occur within 'await' expression

Yeah, I get a warning, but I still don't understand the behavior of one global actor inside another.

When you call into PrettyActor from the main actor, the current execution context in the main actor will be suspended, and execution will continue on the PrettyActor. The MainActor will be freed up to continue running other main actor tasks, and the continuing execution for the myAssertion() call will be isolated to the PrettyActor (which is why your assertion passes).

That the calling context happens to be MainActor isolated is immaterial to your PrettyActor's local context, other than that we will return to the MainActor once execution in the PrettyActor completes. Note that this applies only to the specific call we're examining here, of course. Any async context could have called into PrettyActor.myAssertion() with the same result. This is indeed the whole point of actors, global or not: they are (locally) isolated to their own execution context (modulo features like custom actor executors).

1 Like

Oh, that's unexpected. So the compiler is [mis]interpreting the closure as sync and basically ignoring the await keyword (other than that warning)?

That's really weird. I expected the closure to be inferred as async based on its usage. async blocks don't have to use await (technically - the compiler should warn that they don't need to be async, though).

It does then explain the isolation behaviour, if it's devolved to a sync function call and therefore unilaterally inherits the isolation context of its caller.