I’m trying to figure out how to make sure that a custom CoW collection struct I’m implementing can safely be declared Sendable (albeit with @unchecked.) I found an older thread on the topic, but wasn’t sure of the etiquette here about reawakening old threads, so I’m starting a new one.
I understand that the idiom using isKnownUniquelyReferenced() is thread-safe as long as there aren’t concurrent mutating calls to the same copy of the struct. And from what I’ve read, structs passed between Tasks are copied instead of passed by reference.
…but, then I considered that a Sendable class instance might contain an instance of my struct, which would allow two threads to have references to that object and thus mutate the struct concurrently. (Or is that ruled out because Sendable classes can’t have var properties?)
If that’s true, then how does one adapt the CoW idiom for sendability? I’d hate to have to put mutexes and locks all over the implementation. Can I make the internal state reference atomic?
In order to be Sendable, a class needs to protect its mutable state with some form of synchronization, such as a Mutex. One of the benefits of value types is that they tend to require less manual synchronization, but once you put them inside a reference type you're getting reference type behavior, which includes that.
David's given the general answer, but since you're specifically talking about CoW, it's important to clarify that there's an important exception for unique references. If (1) there's a duration in which you know that you have the only reference to an object, and (2) all such durations are well-ordered with all other accesses to that object, then you do not need synchronization to read or write that object's stored properties during that duration. And it happens to be true that exclusivity and Swift's concurrency safety establish those two guarantees for an object referenced by a memory location that an inout parameter (including self in a mutating method) has been bound to, if isKnownUniquelyReferenced returns true for that location, as long as you never write to the object except within such scopes.