Thank you Michel.
> Rather, use a different copy of the variable to each thread.
How should I copy a variable to each thread?
I’m not sure to understand what you mean by “copy” in this case. If I have this:
var a = Array<Int>()
var b = a
do you consider “b” as being a copy of the variable “a”?
> Copied variables will share the same storage but will make a copy of the
> storage when writing to it.
But by sharing the same storage and making a copy of the storage when writing
to it, there would be a race condition if the storage is being written by
another thread while being copied, right?
> I'm not sure what is the problem with your SynchronizedArray example. But I
> would try reimplementing `var element` this way:
Reimplementing the elements property doesn’t fix the race. Of course, for my
specific example, I can still do:
var elements: Array<Element> {
return access { array in
array.map { $0 }
}
}
but I’d like to know if there is a more generic way to copy a value type
variable in Swift.
> On Dec 5, 2017, at 3:23 PM, Michel Fortin <[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'm not sure what is the problem with your SynchronizedArray example. But I
> would try reimplementing `var element` this way:
>
> var elements: Array<Element> {
> return access { $0 }
> }
>
> If this change fixes the race it means the compiler is making the copy after
> the `unlock()` with your previous code, which could explain the detected
> race. I suppose the additional copy afterwards fixes the race because of an
> internal implementation detail (like changing the reference count for the
> storage). I'd be wary of the optimizer breaking this trick though.
>
>> Le 5 déc. 2017 à 5:20, Romain Jacquinot via swift-users
>> <[email protected] <mailto:[email protected]>> a écrit :
>>
>> Hi,
>>
>> I'm trying to better understand how copy-on-write works, especially in a
>> multithreaded environment, but there are a few things that confuse me.
>>
>> From the documentation, it is said that:
>> "If the instance passed as object is being accessed by multiple threads
>> simultaneously, isKnownUniquelyReferenced(_:) 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."
>>
>> Let's consider this sample code:
>>
>> func mutateArray(_ array: [Int]) {
>> var elements = array
>> elements.append(1)
>> }
>>
>> let q1 = DispatchQueue(label: "testQ1")
>> let q2 = DispatchQueue(label: "testQ2")
>> let q3 = DispatchQueue(label: "testQ3")
>>
>> let iterations = 1000
>>
>> var array: [Int] = [1, 2, 3]
>>
>> q1.async {
>> for _ in 0..<iterations {
>> mutateArray(array)
>> }
>> }
>>
>> q2.async {
>> for _ in 0..<iterations {
>> mutateArray(array)
>> }
>> }
>>
>> q3.async {
>> for _ in 0..<iterations {
>> mutateArray(array)
>> }
>> }
>>
>> // ...
>>
>> From what I understand, since Array<T> implements copy-on-write, the array
>> should be copied only when the mutating append(_:) method is called in
>> mutateArray(_:). Therefore, isKnownUniquelyReferenced(_:) should be called
>> to determine whether a copy is required or not.
>>
>> However, it is being accessed by multiple threads simultaneously, which may
>> cause a race condition, right? So why does the thread sanitizer never detect
>> a race condition here? Is there some compiler optimization going on here?
>>
>> On the other hand, the thread sanitizer always detects a race condition, for
>> instance, if I add the following code to mutate directly the array:
>>
>> for _ in 0..<iterations {
>> array.append(1)
>> }
>>
>> In this case, is it because I mutate the array buffer that is being copied
>> from other threads?
>>
>>
>> Even strangier, let's consider the following sample code:
>>
>> class SynchronizedArray<Element> {
>>
>> // [...]
>>
>> private var lock = NSLock()
>> private var _elements: Array<Element>
>>
>> var elements: Array<Element> {
>> lock.lock()
>> defer { lock.unlock() }
>> return _elements
>> }
>>
>> @discardableResult
>> public final func access<R>(_ closure: (inout T) throws -> R) rethrows
>> -> R {
>> lock.lock()
>> defer { lock.unlock() }
>> return try closure(&_value)
>> }
>> }
>>
>> let syncArray = SynchronizedArray<Int>()
>>
>> func mutateArray() {
>> syncArray.access { array in
>> array.append(1)
>> }
>>
>> var elements = syncArray.elements
>> var copy = elements // [X] no race condition detected by TSan when I
>> add this line
>> elements.append(1) // race condition detected by TSan (if previous line
>> is missing)
>> }
>>
>> // Call mutateArray() from multiple threads like in the first sample code.
>>
>> The line marked with [X] does nothing useful, yet adding this line prevents
>> the race condition at the next line to be detected by the thread sanitizer.
>> Is this again because of some compiler optimization?
>>
>> However, when the array buffer is being copied, we can mutate the same
>> buffer with the append(_:) method, right? So, shouldn't the thread sanitizer
>> detect a race condition here?
>>
>> Please let me know if I ever misunderstood how copy-on-write works in Swift.
>>
>> Also, I'd like to know:
>> - besides capture lists, what are the correct ways to pass a copy-on-write
>> value between threads?
>> - for thread-safe classes that expose an array as a property, should I
>> always copy the private array variable before returning it from the public
>> getter? If so, is there any recommended way to force-copy a value type in
>> Swift ?
>>
>> Any help would be greatly appreciated.
>> Thanks.
>>
>> Note: I'm using Swift 4 with the latest Xcode version (9.2 (9C40b)) and the
>> thread sanitizer enabled.
>>
>> _______________________________________________
>> swift-users mailing list
>> [email protected] <mailto:[email protected]>
>> https://lists.swift.org/mailman/listinfo/swift-users
>
>
>
> --
> Michel Fortin
> https://michelf.ca <https://michelf.ca/>
_______________________________________________
swift-users mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-users