I am implementing a custom Array type for a distributed memory system.
I have a DataView struct which represents the array to the user as a value type, and a backing store class object referenced by the DataView that does all the replication. Write accesses are mutating so they don't cause a problem.
However read only accesses are not mutating and there lies the problem. If the storage object is uniquely referenced, then I can do my business without taking a synchronization lock, otherwise I need to take a lock before syncing memory.
I can "always" take a lock to work around this, but most of the time it isn't necessary and I care about performance.
I think there should be a "read only pass by reference"
I just pulled this comment from the source code. I was under the impression that isKnownUniquelyReferenced is thread safe, but the comments implies that it isn't??
If you implement the copy-on-write backing store correctly, as done with ContiguousArray, then you don’t need to worry about multiple threads sharing the same storage. That’s not a race because none of the threads can mutate the storage without first making a local copy.
So, if you pass the DataView object off to another thread by value, then you’ll be fine. Each thread will have it’s own logical copy of the data. `isUniquelyReferenced` will return `false` for any thread that has a copy.
Now, if the DataView struct is itself a shared object, either by virtue of being stored in a global or a class property, then you have to worry about races. Any thread calling `isUniquelyReferenced` on that object should have exclusive access to the object at that time. In other words, `isUniquelyReferenced` should be thought of as a “write” to the location that stores the reference to your storage. In that sense, `inout` is again semantically correct even though it doesn’t change the value.
It sounds like you want to use the refcount of the data storage as synchronization on the data value itself which doesn’t work. If the data value is in a class property, that class needs its own synchronization. Or just always pass DataView by value and don’t store it in shared class properties.
Hope that makes sense!
On Jan 25, 2017, at 10:53 AM, Edward Connell via swift-users <firstname.lastname@example.org> wrote:
/// If the instance passed as `object` is being accessed by multiple threads
/// simultaneously, this function may still return `true`. Therefore, you must
/// only call this function from mutating methods with appropriate thread
/// synchronization. That will ensure that `isKnownUniquelyReferenced(_:)`
/// only returns `true` when there is really one accessor, or when there is a
/// race condition, which is already undefined behavior.
/// - Parameter object: An instance of a class. This function does *not* modify
/// `object`; the use of `inout` is an implementation artifact.
On Wed, Jan 25, 2017 at 10:31 AM, Joe Groff <email@example.com <mailto:firstname.lastname@example.org>> wrote:
> On Jan 25, 2017, at 10:20 AM, Edward Connell via swift-users <email@example.com <mailto:firstname.lastname@example.org>> wrote:
> I have a data structure that calls isKnownUniquelyReferenced on a member. It forces everything to be marked as mutating because of the inout parameter, however the parameter is never mutated, it is just read right?? The reason it is inout is because a read only reference is required.
> If it is truly not mutating, is there some way around this so I don't have to mark everything in the caller chain as mutating also? It's kind of annoying...
In Swift's current model, `isKnownUniquelyReferenced` needs to be inout because that's currently the only way to assert unique access to an object in memory, since read-only rvalues are otherwise freely copyable so the result of the uniqueness check would be only momentarily valid at best. What are you trying to do that requires using it on a nonmutable value? There may be another way to go about it.
swift-users mailing list