<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="">Thank you Jordan.<div class=""><br class=""></div><div class="">I’ve filed a bug at <a href="http://bugs.swift.org" class="">bugs.swift.org</a>:&nbsp;<a href="https://bugs.swift.org/browse/SR-6543" class="">https://bugs.swift.org/browse/SR-6543</a>.</div><div class=""><br class=""></div><div class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Dec 6, 2017, at 2:47 AM, Jordan Rose &lt;<a href="mailto:jordan_rose@apple.com" class="">jordan_rose@apple.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><br class="Apple-interchange-newline"><br class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><blockquote type="cite" class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;"><div class="">On Dec 5, 2017, at 16:24, Romain Jacquinot &lt;<a href="mailto:rjacquinot@me.com" class="">rjacquinot@me.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><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 class="" style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);"><span class="" style="color: rgb(186, 45, 162);">public</span><span class="Apple-converted-space">&nbsp;</span><span class="" style="color: rgb(186, 45, 162);">var</span><span class="Apple-converted-space">&nbsp;</span>myArray:<span class="Apple-converted-space">&nbsp;</span><span class="" style="color: rgb(112, 61, 170);">Array</span>&lt;<span class="" style="color: rgb(112, 61, 170);">Int</span>&gt; {</div><div class="" style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);">&nbsp; &nbsp; lock.lock()</div><div class="" style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);">&nbsp; &nbsp;<span class="Apple-converted-space">&nbsp;</span><span class="" style="color: rgb(186, 45, 162);">defer</span><span class="Apple-converted-space">&nbsp;</span>{ lock.unlock() }</div><div class="" style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);">&nbsp; &nbsp; _myArray</div><div class="" style="margin: 0px; font-stretch: normal; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);">}</div></div></div></div></blockquote><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class=""></div><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class="">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="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><blockquote type="cite" class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;"><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;">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&nbsp;array: [Int] = [1,&nbsp;2,&nbsp;3]<br class=""><br class="">let&nbsp;iterations =&nbsp;1_000_000<br class=""><br class="">var&nbsp;copy1 = array<br class="">q1.async&nbsp;{<br class="">&nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;copy1.append(i)<br class="">&nbsp; &nbsp;&nbsp;}<br class="">}<br class=""><br class="">var&nbsp;copy2 = array<br class="">q2.async&nbsp;{<br class="">&nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;copy2.append(i)<br class="">&nbsp; &nbsp;&nbsp;}<br class="">}<br class=""><br class="">var&nbsp;copy3 = array<br class="">q3.async&nbsp;{<br class="">&nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;copy3.append(i)<br class="">&nbsp; &nbsp;&nbsp;}<br class="">}<br class=""><br class="">for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp;&nbsp;array.append(i)<br class="">}<br class=""><br class="">q1.sync&nbsp;{}<br class="">q2.sync&nbsp;{}<br class="">q3.sync&nbsp;{}<br class="">NSLog("done")<br class=""></div></div></div></blockquote><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class=""></div><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class="">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 style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class=""></div><br class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;"><blockquote type="cite" class="" style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px;"><div class=""><div class="" style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;"><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&nbsp;array: [Int] = [1, 2,&nbsp;3]<div class=""><br class="">let&nbsp;iterations =&nbsp;1_000_000<br class=""><br class="">q1.async&nbsp;{ [array]&nbsp;in<br class="">&nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;var&nbsp;copy = array<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;copy.append(i)<br class="">&nbsp; &nbsp;&nbsp;}<br class="">}<br class=""><br class="">q2.async&nbsp;{ [array]&nbsp;in<br class="">&nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;var&nbsp;copy = array<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;copy.append(i)<br class="">&nbsp; &nbsp;&nbsp;}<br class="">}<br class=""><br class="">q3.async&nbsp;{ [array]&nbsp;in<br class="">&nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;var&nbsp;copy = array<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;copy.append(i)<br class="">&nbsp; &nbsp;&nbsp;}<br class="">}<br class=""><br class="">for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp;&nbsp;array.append(i)<br class="">}<div class=""><br class=""></div><div class="">q1.sync&nbsp;{}<br class="">q2.sync&nbsp;{}<br class="">q3.sync&nbsp;{}<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&nbsp;array: [Int] = [1,&nbsp;2,&nbsp;3]<br class=""><br class="">let&nbsp;iterations =&nbsp;1_000_000<br class=""><br class="">let&nbsp;copy1 = array<br class="">q1.async {<br class="">&nbsp; &nbsp;&nbsp;var&nbsp;copy1 = copy1<br class="">&nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;copy1.append(i)<br class="">&nbsp; &nbsp;&nbsp;}<br class="">}<br class=""><br class=""><br class="">let&nbsp;copy2 = array<br class="">q2.async {<br class="">&nbsp; &nbsp;&nbsp;var&nbsp;copy2 = copy2<br class="">&nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;copy2.append(i)<br class="">&nbsp; &nbsp;&nbsp;}<br class="">}<br class=""><br class="">let&nbsp;copy3 = array<br class="">q3.async {<br class="">&nbsp; &nbsp;&nbsp;var&nbsp;copy3 = copy3<br class="">&nbsp; &nbsp;&nbsp;for&nbsp;i&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;copy3.append(i)<br class="">&nbsp; &nbsp;&nbsp;}<br class="">}<br class=""><br class="">for&nbsp;_&nbsp;in&nbsp;0..&lt;iterations {<br class="">&nbsp; &nbsp;&nbsp;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 style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class=""></div><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class="">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 style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class=""></div><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class="">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 style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><br class=""></div><div style="font-family: Helvetica; font-size: 12px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class="">Jordan</div></div></blockquote></div><br class=""></div></body></html>