A lot of online resources say non-final classes are not Sendable by default. That means classes and their properties are not thread-safe. So why is this code not raising an error:
class Counter {
var count = 0
func increment() {
count += 1
}
}
class Tester {
var counter = Counter()
func mutate() {
Task {
counter.increment()
}
}
}
I was expecting an error to be raised at counter.increment() since counter isn't Sendable.
Can anyone tell why this code isn't raising an error?
You are not getting an error/warning because, as of Xcode 14.3, at least, the compiler will only perform “minimal” checks re
Sendable. So, set the “Strict Concurrency Checking” setting to “Complete”:Next, the first problem with your code snippet at this point is really that
Testeris notSendable:But let’s assume that you get past that, either by making
TesteraSendabletype or simply capture theCounter, rather thanTester. Then the compiler will report theSendableproblem, this time regardingCounter:So, you asked:
Yes, classes are not
Sendableby default.To be more precise, it just means that you have not informed the Swift concurrency system whether it is thread-safe or not. It might be. It might not be. (In this case, it is not.) You simply do not get
Sendableinference for free, even if it was internally thread-safe, as you would with astructoractor.Specifically, if a class is
finaland has no mutable properties, it is generally thread-safe, but is not automaticallySendable. But you can just addSendableconformance to let the compiler know that it really is. However, if the class has some mutable properties and you want to mark it asSendable, you have to manually make it thread-safe with some manual synchronization mechanism for its mutable properties and, then, when you are done, you can mark this class as@unchecked Sendableto let the compiler know that although the compiler cannot reasonable verify whether it is reallySendableor not, but that you are vouching for it.But, in short, your online resources are correct and classes are not
Sendableby default. You just need to bump up the “Strict Concurrency Checking” setting in order to see all theseSendable-related warnings.While I suspect you know this, for the sake of other readers, we can demonstrate the lack of thread-safety with a unit test:
Or turn on TSAN, and it will produce:
But if you either add manual synchronization of the mutable variable,
count, inCounter, or just make it an actor, the data race is resolved and it is thread-safe. E.g.: