Hi Jens,
In the SynchronizedArray class, I use a lock to perform mutating operations on
the array. However, my questions concern the case where an array is exposed as
a public property of a thread-safe class, like this:
public class MyClass {
private var _myArray: Array<Int> = []
private var _lock = NSLock()
public var myArray: Array<Int> {
_lock.lock()
defer { _lock.unlock() }
return _myArray
}
public func doSomethingThatMutatesArray() {
_lock.lock()
_myArray.append(1)
_lock.unlock()
}
}
Arrays are implemented as structs in Swift. This is a value type, but because
Array<T> implements copy-on-write, there is an issue if you do something like
this from multiple threads:
let myClass = MyClass()
func callFromMultipleThreads() {
let arrayCopy = myClass.myArray
arrayCopy.append(2) // race condition here
}
This is quite problematic, because the user of MyClass shouldn’t have to worry
about side-effects when using a copy of the value from myArray.
> On Dec 5, 2017, at 8:22 PM, Jens Alfke <[email protected]> wrote:
>
>
>
>> On Dec 5, 2017, at 6:24 AM, Michel Fortin via swift-users
>> <[email protected] <mailto:[email protected]>> wrote:
>>
>> The array *storage* is copy on write. The array variable (which essentially
>> contains a pointer to the storage) is not copy on write. If you refer to the
>> same array variable from multiple threads, you have a race. Rather, use a
>> different copy of the variable to each thread. Copied variables will share
>> the same storage but will make a copy of the storage when writing to it.
>
> I think you’ve misunderstood. The race condition Romain is referring to is
> when the two threads both access the shared storage, through separate array
> variables.
>
> Romain:
> From the thread sanitizer warnings, it sounds like the implementation of
> Array is not using synchronization around its call(s) to
> isKnownUniquelyReferenced … which would mean the class is not thread-safe.
>
> It’s pretty common for the regular (mutable) collection classes supplied by a
> framework to be non-thread-safe; for example consider Foundation and Java.
> The reason is that the overhead of taking a lock every time you access an
> array element is pretty high. Generally it’s preferable to use
> larger-granularity locks, i.e. grab an external lock before performing a
> number of array operations.
>
> —Jens
_______________________________________________
swift-users mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-users