<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Dec 5, 2017, at 16:24, Romain Jacquinot <<a href="mailto:rjacquinot@me.com" class="">rjacquinot@me.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><meta http-equiv="Content-Type" content="text/html; charset=utf-8" class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class=""><blockquote type="cite" class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><div class="">Ah, it's a Swift bug in the SynchronizedArray / MyClass cases</div></div></blockquote><br class=""></div><div class="">If I remove the intermediate copy in the myArray getter, should it ā without Swift bug ā also return a unique copy?</div><div class=""><br class=""></div><div class=""><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""><span style="color: #ba2da2" class="">public</span> <span style="color: #ba2da2" class="">var</span> myArray: <span style="color: #703daa" class="">Array</span><<span style="color: #703daa" class="">Int</span>> {</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> lock.lock()</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #ba2da2" class="">defer</span> { lock.unlock() }</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> _myArray</div><div style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class="">}</div></div></div></div></blockquote><div><br class=""></div><div>Yes. As soon as you treat it as a value (including returning it), it should be independent. The assignment to a local variable doesn't really add anything interesting, and the optimizer will throw it out.</div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">By the way, here is a simpler sample code where TSan also detects a race condition:<br class=""><br class="">// Code sample A<div class=""><br class="">var array: [Int] = [1, 2, 3]<br class=""><br class="">let iterations = 1_000_000<br class=""><br class="">var copy1 = array<br class="">q1.async {<br class=""> for i in 0..<iterations {<br class=""> copy1.append(i)<br class=""> }<br class="">}<br class=""><br class="">var copy2 = array<br class="">q2.async {<br class=""> for i in 0..<iterations {<br class=""> copy2.append(i)<br class=""> }<br class="">}<br class=""><br class="">var copy3 = array<br class="">q3.async {<br class=""> for i in 0..<iterations {<br class=""> copy3.append(i)<br class=""> }<br class="">}<br class=""><br class="">for i in 0..<iterations {<br class=""> array.append(i)<br class="">}<br class=""><br class="">q1.sync {}<br class="">q2.sync {}<br class="">q3.sync {}<br class="">NSLog("done")<br class=""></div></div></div></blockquote><div><br class=""></div><div>Yes, this would be failing for the same reason (didn't test it). The mutations are operating on different Array variables, but they start off sharing the same storage, and that means we can get into the same situation where one queue thinks it can modify the storage in place while another queue is still copying the initial contents.</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><div style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class=""><div class="">I can avoid the race condition to occur by using a capture list:<br class=""><br class="">// Code sample B<div class=""><br class="">var array: [Int] = [1, 2, 3]<div class=""><br class="">let iterations = 1_000_000<br class=""><br class="">q1.async { [array] in<br class=""> for i in 0..<iterations {<br class=""> var copy = array<br class=""> copy.append(i)<br class=""> }<br class="">}<br class=""><br class="">q2.async { [array] in<br class=""> for i in 0..<iterations {<br class=""> var copy = array<br class=""> copy.append(i)<br class=""> }<br class="">}<br class=""><br class="">q3.async { [array] in<br class=""> for i in 0..<iterations {<br class=""> var copy = array<br class=""> copy.append(i)<br class=""> }<br class="">}<br class=""><br class="">for i in 0..<iterations {<br class=""> array.append(i)<br class="">}<div class=""><br class=""></div><div class="">q1.sync {}<br class="">q2.sync {}<br class="">q3.sync {}<br class="">NSLog("done")<br class=""><br class="">or by using a constant copy, which, if Iām not wrong, should behave the same way than the capture list:<div class=""><br class=""></div><div class="">// Code sample C</div><div class=""><br class=""></div><div class="">var array: [Int] = [1, 2, 3]<br class=""><br class="">let iterations = 1_000_000<br class=""><br class="">let copy1 = array<br class="">q1.async {<br class=""> var copy1 = copy1<br class=""> for i in 0..<iterations {<br class=""> copy1.append(i)<br class=""> }<br class="">}<br class=""><br class=""><br class="">let copy2 = array<br class="">q2.async {<br class=""> var copy2 = copy2<br class=""> for i in 0..<iterations {<br class=""> copy2.append(i)<br class=""> }<br class="">}<br class=""><br class="">let copy3 = array<br class="">q3.async {<br class=""> var copy3 = copy3<br class=""> for i in 0..<iterations {<br class=""> copy3.append(i)<br class=""> }<br class="">}<br class=""><br class="">for _ in 0..<iterations {<br class=""> array.append(array.count)<br class="">}<br class=""><br class="">q1.sync {}<br class="">q2.sync {}<br class="">q3.sync {}<br class="">NSLog("done")<br class=""></div></div></div></div></div></div></div></blockquote><div><br class=""></div><div>Yep, these two are equivalent, at least as far as this issue goes. This dodges the issue because the capture or the 'let' keeps an extra reference to the original array at all times, meaning that none of the mutations are ever done in-place.</div><div><br class=""></div><div>Thank you again for catching this! (It's also possible we've already fixed it on master, but it's still worth having a bug report so we can add a regression test.)</div><div><br class=""></div><div>Jordan</div></div></body></html>