Promise is non-sendable type

Hey guys,

Enabling StrictConcurrency check raise a warning on our Future extension:

extension Future where Failure == Error, Output: Sendable {

    convenience public init(_ asyncFunction: @escaping @Sendable () async throws -> Output) {
        self.init { promise in
            Task {
                do {
                    let result = try await asyncFunction()
                    promise(.success(result))
                } catch {
                    promise(.failure(error))
                }
            }
        }
    }
}

The warning is: Capture of 'promise' with non-sendable type '(Result<Output, any Error>) -> Void' in a @Sendable closure

I'm trying to understand the reason behind. I've read here that

closure must not close over non-Sendable

But why promise is non-Sendable? Result is enum with only Sendable associated values (both Output and Failure)

Looks like I'm still missing key point of @Sendable

Hmmmmmm looks like the promise closure isn't marked Sendable.

1 Like

Just to elaborate a bit on @AlexanderM's answer here, the possible sendability of a closure expression can depend on state that is not reflected in the parameter-and-return-type signature of the function type. An arbitrary function value of type (Result<some Sendable, some Sendable & Error>) -> Void may be sendable, but without visibility into the definition of the function itself, we can't know for sure. So your use of promise can't just assume that "sendable parameters and sendable return value means sendable," the author of the API providing the promise parameter needs to explicitly mark the function type @Sendable in order for us to rely on it. The fact that it hasn't been marked as @Sendable may be an oversight, or there may be some fundamental implementation reason that promise cannot be sent across isolation domains.

5 Likes