Closure isolation control

Yeah, the problems with weak isolation are all around what you do if the actor reference is nil. If isolation safety were just about accessing the actor's properties, it'd be fine to degrade a weakly-isolated function to being non-isolated when the actor is destroyed, since of course there's no way to access a deallocated actor's protected state. But actor isolation also provides isolation to non-sendable values in the actor's region, and degrading to non-isolated would mean concurrently accessing those values. So allowing weak isolation means the compiler also has to do a lot of subtle reasoning about whether the actor is still valid, including preventing access to certain captured values. I think it's reasonable to put it down as a possible future direction.

7 Likes

From code isolated to a weak actor reference, I would expect it to run on global executor until strong reference is obtained and run on the actor after strong reference is obtained. So obtaining strong reference should be a hop, and require an await keyword in some form.

{ [isolated weak a] in 
   // global executor
   if await let strongA = a {
     // a
   }
   // global executor
}

Which implies that if ever supported, closures isolated to weak actor reference must be async closures to be able to hop.


BTW, does the proposal apply to the synchronous closures at all? I could not find this in the text.

For synchronous closures, isolation is an important part of the type, which cannot be erased. @MainActor () -> () and () -> () are two different types. What would be the type of a synchronous closure isolated to a captured actor instance? It cannot be type without isolation, because that allows to call the closure in any isolation context.

1 Like

It is no different (and no more or less problematic) than the existing isolated parameters design:

1 Like

Good question and I have added a paragraph about this to the "detailed design" section.

1 Like

Is this still the plan for some future version of Swift, or has it been superseded by a different proposal?

7 Likes

Closure isolation control and isolated captures have not been superseded and this is still a valuable proposal to pursue. There's an in-progress implementation that needs to be completed before the proposal is ready to be put into formal review.

10 Likes

Is there a workaround to achieve something like this in current Swift (6.0)?

final class Foo {
  func update() { }
}

func foo(isolation: isolated any Actor, foo: Foo) {
  Task<Void, Never> { /* [isolated isolation] in */
    foo.update()
  }
}

UPDATE
I've come up with the following which is a little nasty but seems like it should work?


final class Foo {
  func update() { }
}

func foo(isolation: isolated any Actor, foo: Foo) {
  let helper = ClosureIsolationHelper(isolation: isolation, foo: foo)
  Task<Void, Never> {
    await helper.withIsolatedFoo { foo in
      foo.update()
    }
  }
}

private struct ClosureIsolationHelper: @unchecked Sendable {
  func withIsolatedFoo(
    _ body: @Sendable (Foo) async -> Void
  ) async {
    await withIsolatedFoo(body, isolation: isolation)
  }
  
  init(isolation: any Actor, foo: Foo) {
    self.isolation = isolation
    self.foo = foo
  }
  private func withIsolatedFoo(
    _ body: (Foo) async -> Void,
    isolation: isolated any Actor
  ) async -> Void {
    foo.update()
  }
  private let isolation: any Actor
  private let foo: Foo
}

There is! You should be able to get the isolation inheritance you need like this:

func foo(isolation: isolated any Actor, foo: Foo) {
  Task<Void, Never> {
    _ = isolation // explicit capture
    foo.update()
  }
}
5 Likes

Oh cool that is much cleaner, thanks!

3 Likes

lol, I lazily defined and called some network request methods in SwiftUI-View structs until i realised that its documentation says @MainActor

Can you help me understand how this is related to the source-breaking nature of the proposal?

Also, adding network calls in a MainActor type doesn't sound like an issue to me, since they will not run synchronously, but perhaps there is more going on?