public final
class NonSendable
{
var x:Int = 0
init()
{
}
}
but i can still pass instances of it to async functions from inside an actor-isolated method, if the instance is a local variable and not shared by the actor.
func doSomethingAsyncWithNonSendable(_ instance:NonSendable) async
{
}
actor A
{
init()
{
}
func test() async
{
let instance:NonSendable = .init()
await doSomethingAsyncWithNonSendable(instance)
}
}
but if i am more explicit about the non-sendability of NonSendable:
warning: non-sendable type 'NonSendable' exiting actor-isolated context
in call to non-isolated global function 'doSomethingAsyncWithNonSendable'
cannot cross actor boundary
await doSomethingAsyncWithNonSendable(instance)
^
is this a bug? or does @available(*, unavailable) actually mean something different from saying the type is merely not Sendable?
See SE-0337. Because most modules have not yet been annotated for sendability, the current default sendability checking mode only produces warnings for things which have been explicitly annotated.
-strict-concurrency=complete switches to the Swift 6 mode where anything not marked as Sendable is considered non-Sendable.
The extension marks the type as “explicitly non-Sendable”, so the compiler is more aggressive about telling you about it not being Sendable. Basically, it distinguishes between “we’ve looked at this type and it’s definitely not Sendable” and “we haven’t looked at this type so we don’t know if it’s Sendable”. In a 100% Swift 6 world, that difference won’t really matter anymore, but in a world full of Swift 5 code, it still does.
(Hmm, @nonSendable would make a good peer macro…)
why is it that i can do this within a nonisolated actor method, and call it from an isolated method, but i cannot inline the nonisolated method into the isolated method?
In settling upon a syntax for noncopyable types, it was contemplated that it should be generalizable to ~Sendable on the primary type declaration to suppress a presumption to the contrary. If we can do that, we shouldn’t need a macro to generate an unavailable extension in order to accomplish this.
Good point. However, is this message displayed at the point when a concurrency warning is displayed? Even if it were displayed, is it actionable information? If a type isn’t Sendable, the reason for that isn’t something that an end user can do anything about.
(Incidentally, types with reference semantics can nonetheless be Sendable, so that can’t be actually the full story.)
when i get a “Sendable” warning, my first thought is usually not “there is something wrong with this code”, my first thought is usually “whoever wrote this library (which is sometimes me) forgot to annotate this type with Sendable”. so it saves me time if i am told the lack of Sendable is intentional and not just a missing annotation.
but that’s all moot because (although i swear i’ve seen the message get printed before) i could not get it to show up on 5.8 when compiling a quick experiment that misuses the type. but maybe it should?
a MongoDB session is stateful, on both the client and server sides. even if it were made threadsafe on the client side, it would still be incorrect usage.
i thought about this some more and i think i finally understand why we can’t inline non-isolated stuff into isolated contexts, because when we declare a reference in an isolated context, it has the right to escape its original concurrency domain via assignment to shared actor state.
and the right to escape to the shared actor state is mutually exclusive with the right to escape to some external async task, because when the async call suspends, the actor doesn’t wait for it to return, it starts executing something else in its queue, and now session exists in two different concurrency timelines.
but if we declare a reference in a non-isolated context, it doesn’t have the right to escape to self, so it is fine to pass it to another async function.
extension MyActor
{
nonisolated
func notIsolated()
{
let session:Mongo.Session = .init()
self.sessions.append(session)
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ not allowed
}
}
i wonder if what we need is some kind of nonisolated do, like: