I've been trying to figure this out and I think the answer is no, but I'd like to check:
Is it possible for a non-Sendable
type to perform some detached asynchronous operation, with a completion handler that calls back to some method on the instance?
final class NonSendable {
func doSomething() {
Task.detached {
// <... do some work ...>
// < -- HERE -- >
self.otherFunc()
}
}
func otherFunc() {
}
}
At the line marked HERE
, as far as I can tell, there is no way to call otherFunc
.
So I was trying to figure what a non-Sendable type even means in Swift, and I found this from the region-based isolation proposal:
As defined above, all non-
Sendable
values in a Swift program belong to some isolation region. An isolation region is isolated to an actor's isolation domain, a task's isolation domain, or disconnected from any specific isolation domain:[...]
As the program executes, an isolation region can be passed across isolation boundaries, but an isolation region can never be accessed by multiple isolation domains at once. When a region R1 is merged into another region R2 that is isolated to an actor, R1 becomes protected by that isolation domain and cannot be passed or accessed across isolation boundaries again.
If an isolation region (containing a non-Sendable type) is able to move across isolation domains, then it would make sense why it is not possible to safely invoke a callback from some arbitrary other domain - because you don't know whether the region has moved since your task was launched.
In other words, these kind of functions "pin" your non-sendable type (and its isolation region) to the isolation domain they currently live on, until some point which cannot be reasoned about statically (so indefinitely). That's fine for the use-case I have in mind, but I don't believe it's expressible in the language (is it?). I couldn't find anything like that in the proposal or future directions, but I may have missed it.
There are quite a lot of types which are not Sendable
, i.e. they cannot be used concurrently from different isolation domains, but you can create them from any isolation domain and then (maybe directly from creation or once you start using them) safely use them only from that same domain. And that is how they are used, and in theory they can support asynchronous callbacks, but I can't find anything in Swift concurrency to model this.
Have I overlooked something? Is there a solution for these kinds of types either currently or planned?