Using `withCheckedContinuation`, etc., in an actor?

Noticed a new warning recently when trying to invoke these functions in an actor:

actor Foo {
  func bar() async {
    await withCheckedContinuation { _ in ... } ⚠️
  }
}

:warning: Non-sendable type '(CheckedContinuation<(), Never>) -> Void' exiting actor-isolated context in call to non-isolated global function 'withCheckedContinuation(function:_:)' cannot cross actor boundary

Is this not legit to do? Should I be using an AsyncStream for this kinda thing?

2 Likes

It's fine to do with withCheckedContinuation specifically. We had a bug at one point where we were diagnosing this incorrectly, but it should be fixed now. What toolchain are you using? @Douglas_Gregor, do you remember the exact release history here?

1 Like

This is in Xcode 14 beta 3, so maybe using a build that predates the fix?

I only fixed this a couple of days ago, so it did not make beta 3: Calling `@_unsafeInheritExecutor` functions doesn't exit the current actor by DougGregor · Pull Request #59873 · apple/swift · GitHub

Doug

7 Likes

Ah, sorry about the confusion, I was probably confusing the recent fix with some older change.

@Douglas_Gregor I'm not following very closely apple/swift repo, but wondering if you were able also to fix some warnings which apply to @propertyWrappers, in Xcode 14 beta 3 i see many of:

public actor SomeActor {
    @ValuePublished
    nonisolated public private(set) var state: Web3.State

    nonisolated public let statePublisher: AnyPublisher<Web3.State, Never>

    public convenience init(home: Home) {
        self.init(
            state: .loaded(
                Web3.State.StateModel(
                    home: home,
                    selectedWallet: .metaMask
                )
            )
        )
        Task {
            await setup()
        }
    }

    private init(state: Web3.State) {
            self.state = state
            self.statePublisher = _state
                .removeDuplicates()
                .dropFirst()
                .receive(on: DispatchQueue.main)
                .eraseToAnyPublisher()
        }
}

I am getting: Cannot access property '_state' here in non-isolated initializer; this is an error in Swift 6 and Cannot access property 'statePublisher' here in non-isolated initializer; this is an error in Swift 6.

No warnings for Xcode 13.

I'm seeing a similar problem with withTaskCancellationHandler, giving a warning in Xcode 14b5:

actor MyActor {

    private func foo() async -> Result<A, B> {
        let x = createSomethingBasedOnActorState()
        return await withTaskCancellationHandler {
            await withCheckedContinuation { continuation in
                x.doWorkWith(continuation: continuation)
            }
        } onCancel: {
            x.finishUp(returning: .failure(.cancelled))
        }
    }
Non-sendable type '() async throws -> Result<A, B>' exiting
actor-isolated context in call to non-isolated global function
'withTaskCancellationHandler(operation:onCancel:)'
cannot cross actor boundary

Is this legit? Does withTaskCancellationHandler exit the current actor?

1 Like

It does; it will be invoked on "some" task, and you don't know where.

Fixing this to be an async closure or even inherit the actor context is sadly not trivial... It's also not necessarily a "fix" but a bigger tradeoff in how cancellation and task locking works...

Worth filing a bug and/or voicing this as a problem, it definitely comes up here and there.