Hi, I have two questions on the Motivation section of SE-0430, one about a detail of region based isolation rules, another about actor initializer.
Below is the example code in the section (there are multiple examples, I’ll focus on this one):
class NonSendable {}
actor MyActor {
let ns: NonSendable
init(ns: NonSendable) {
self.ns = ns
}
}
func send(ns: NonSendable) {
// error: sending 'ns' may cause a data race
// note: task-isolated 'ns' to actor-isolated 'init' could cause races between
// actor-isolated and task-isolated uses.
let myActor = MyActor(ns: ns)
}
func callSend() {
let ns = NonSendable()
send(ns: ns)
print(ns)
}
Q1) This is a minor question about a detail of region based isolation rules.
According the comments in above code, the authors thought the ns
parameter of send(ns:)
function is in task isolated region and hence couldn’t be transferred. As the function is a synchronous function, I wonder if that is true? Below is what I found in SE-0414 (emphasis mine):
A task isolated isolation region consists of values that are isolated to a specific task. This can only occur today in the form of the parameters of nonisolated asynchronous functions since unlike actors, tasks do not have non- Sendable state that can be isolated to them.
Let’s assume the authors are correct in Q1 (I believe they are. I just would like to confirm it), I have another more important question.
Q2) This question is about actor initializer.
The authors said the following before they gave the above example:
Actor initializers are nonisolated , so a call to an actor initializer does not cross an isolation boundary, meaning the argument values would be usable in the caller after the initializer returns under the standard region isolation rules.
IIUC what the authors meant was that a) the code should have been OK, and b) Region Based Isolation rules prevented it from compiling and hence the need for the new sending
keyword.
I’m not sure about the above statement. From my understanding of SE-0327 (actor initializer), the self
parameter passed to an actor’s synchronous initializer might decays to non-isolated. I wonder why the authors said “Actor initializers are (always) nonisolated”? In my understanding, in this specific example what's important is not if the self
(and hence the initializer) is non-isolated but if the ns parameter value in the send(ns:)
function and the ns property in the actor are in the same isolation domain. It happens that the ns property in the actor is immutable, so it's in non-isolation domain indeed. But I don't find the authors emphasize it. What if it's a mutable property? Is the above statement still true? If not, how can the sending
keyword differentiate the two cases? I think I must be misunderstanding something. Thanks for any explanation.