Two questions on nonisolated

Hi, I have two questions about nonisolated after reading SE-0313. I'd appreciate any explanations or comments.

Q1: Why is it OK to apply nonisolated to a global variable?

IIUC nonisolated was introduced to allow accessing to actor's property or method synchronously from outside the actor. So I thought it's only supposed to be used inside actor. However, I find the following code compiles:

final class SomeClass: Sendable {
    let x = 1
}

nonisolated let g = SomeClass() // This compiles

I wonder what's the effect of nonisolated modifier in the above code?

Q2: On which executor is a nonisolated method executed?

At first I thought that a nonisolated method is executed on global pool (the global executor). However, in the followinng code I'm able to access a nonisolated method synchronously from within the actor, which I think indicates nonisolated method is executed on the actor's executor. On the other hand, the fact that one is able to access an actor's nonisolated property and method synchronously from outside the actor indicates that they are executed in global executor. So, does it mean where a nonisolated method is executed depends on the caller?

final class SomeClass: Sendable {
    let x = 1
}

actor BankAccount {
    nonisolated let o = SomeClass()

    nonisolated func f1() -> SomeClass {
         o
    }

    func f2() -> SomeClass {
        // It's OK to access nonisolated property and method synchronously
        // from within the actor 
        let _ = o  
        return f1()
    }
}

func test() {
    let account = BankAccount()
    // It's OK to access an actor's nonisolated property and method synchronously
    // from outside the actor  (this is why it was introduced in SE-0313).
    print(account.f1().x)
}

EDIT: for the second question, I find the answer in SE-0420:

Non-isolated synchronous functions dynamically inherit the isolation of their caller.

2 Likes

IIRC, in the script apps all the global code are implicitly isolated to main actor, so it makes sense to be able to opt-out isolation.

Synchronous functions inherit executor from the caller, nonisolated async functions are executed on a generic one.

That seems perfectly fine to me — you cannot access isolated from it, since that is unsafe, but I don’t see nothing wrong in reverse case.

2 Likes

Thanks. All you said make sense to me.

I don't think it's that simple.

  • If the nonisolated method is an async func, we need to access it asynchronously (of course) from within actor. Since nonisolated async func runs in the global executor, there is concurrency domain crossing.
  • If the nonisolated method is an synchronous func, there is no concurrency domain crossing.

When I first tried to understand how to access nonisolated method from within actor, I didn't realize it mattered if it's a sync func or async func. Instead I thought there would be a single general rule for actor-isolated method to call non-isolated method. That's why I got confused. Even if I have figured out the details now, I still find it a bit tricky.

2 Likes

I omitted cases for async, you’re right :slight_smile: For async behavior you need some time and amount of code to get used to it.