<html><head><meta http-equiv="Content-Type" content="text/html; charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Small typo in the second sample code. Should read:<div class=""><br class=""><div class=""><div class=""><pre style="white-space: pre-wrap; background-color: rgb(255, 255, 255);" class="">return try closure(&amp;_elements)  </pre></div></div><div class=""><br class=""></div><div><br class=""><blockquote type="cite" class=""><div class="">Begin forwarded message:</div><br class="Apple-interchange-newline"><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px;" class=""><span style="font-family: -webkit-system-font, Helvetica Neue, Helvetica, sans-serif; color:rgba(0, 0, 0, 1.0);" class=""><b class="">From: </b></span><span style="font-family: -webkit-system-font, Helvetica Neue, Helvetica, sans-serif;" class="">Romain Jacquinot via swift-users &lt;<a href="mailto:swift-users@swift.org" class="">swift-users@swift.org</a>&gt;<br class=""></span></div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px;" class=""><span style="font-family: -webkit-system-font, Helvetica Neue, Helvetica, sans-serif; color:rgba(0, 0, 0, 1.0);" class=""><b class="">Subject: </b></span><span style="font-family: -webkit-system-font, Helvetica Neue, Helvetica, sans-serif;" class=""><b class="">[swift-users] Data races with copy-on-write</b><br class=""></span></div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px;" class=""><span style="font-family: -webkit-system-font, Helvetica Neue, Helvetica, sans-serif; color:rgba(0, 0, 0, 1.0);" class=""><b class="">Date: </b></span><span style="font-family: -webkit-system-font, Helvetica Neue, Helvetica, sans-serif;" class="">December 5, 2017 at 11:20:46 AM GMT+1<br class=""></span></div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px;" class=""><span style="font-family: -webkit-system-font, Helvetica Neue, Helvetica, sans-serif; color:rgba(0, 0, 0, 1.0);" class=""><b class="">To: </b></span><span style="font-family: -webkit-system-font, Helvetica Neue, Helvetica, sans-serif;" class=""><a href="mailto:swift-users@swift.org" class="">swift-users@swift.org</a><br class=""></span></div><div style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px;" class=""><span style="font-family: -webkit-system-font, Helvetica Neue, Helvetica, sans-serif; color:rgba(0, 0, 0, 1.0);" class=""><b class="">Reply-To: </b></span><span style="font-family: -webkit-system-font, Helvetica Neue, Helvetica, sans-serif;" class="">Romain Jacquinot &lt;<a href="mailto:rjacquinot@me.com" class="">rjacquinot@me.com</a>&gt;<br class=""></span></div><br class=""><div class=""><div class="">Hi,<br class=""><br class="">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.<br class=""><br class="">From the documentation, it is said that:<br class="">"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."<br class=""><br class="">Let's consider this sample code:<br class=""><br class="">func mutateArray(_ array: [Int]) { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>var elements = array &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>elements.append(1) &nbsp;<br class="">} &nbsp;<br class=""><br class="">let q1 = DispatchQueue(label: "testQ1") &nbsp;<br class="">let q2 = DispatchQueue(label: "testQ2") &nbsp;<br class="">let q3 = DispatchQueue(label: "testQ3") &nbsp;<br class=""><br class="">let iterations = 1000 &nbsp;<br class=""><br class="">var array: [Int] = [1, 2, 3] &nbsp;<br class=""><br class="">q1.async { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>for _ in 0..&lt;iterations { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>mutateArray(array) &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>} &nbsp;<br class="">} &nbsp;<br class=""><br class="">q2.async { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>for _ in 0..&lt;iterations { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>mutateArray(array) &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>} &nbsp;<br class="">} &nbsp;<br class=""><br class="">q3.async { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>for _ in 0..&lt;iterations { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>mutateArray(array) &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>} &nbsp;<br class="">} &nbsp;<br class=""><br class="">// ... &nbsp;<br class=""><br class="">From what I understand, since Array&lt;T&gt; 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.<br class=""><br class="">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?<br class=""><br class="">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:<br class=""><br class="">for _ in 0..&lt;iterations { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>array.append(1) &nbsp;<br class="">} &nbsp;<br class=""><br class="">In this case, is it because I mutate the array buffer that is being copied from other threads?<br class=""><br class=""><br class="">Even strangier, let's consider the following sample code:<br class=""><br class="">class SynchronizedArray&lt;Element&gt; { &nbsp;<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>// [...] &nbsp;<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>private var lock = NSLock() &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>private var _elements: Array&lt;Element&gt; &nbsp;<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>var elements: Array&lt;Element&gt; { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>lock.lock() &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>defer { lock.unlock() } &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return _elements &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>} &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>@discardableResult &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>public final func access&lt;R&gt;(_ closure: (inout T) throws -&gt; R) rethrows -&gt; R { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>lock.lock() &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>defer { lock.unlock() } &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>return try closure(&amp;_value) &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>} &nbsp;<br class="">} &nbsp;<br class=""><br class="">let syncArray = SynchronizedArray&lt;Int&gt;() &nbsp;<br class=""><br class="">func mutateArray() { &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>syncArray.access { array in &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span><span class="Apple-tab-span" style="white-space:pre">        </span>array.append(1) &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>} &nbsp;<br class=""><br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>var elements = syncArray.elements &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>var copy = elements // [X] no race condition detected by TSan when I add this line &nbsp;<br class=""><span class="Apple-tab-span" style="white-space:pre">        </span>elements.append(1) // race condition detected by TSan (if previous line is missing) &nbsp;<br class="">} &nbsp;<br class=""><br class="">// Call mutateArray() from multiple threads like in the first sample code. &nbsp;<br class=""><br class="">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?<br class=""><br class="">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?<br class=""><br class="">Please let me know if I ever misunderstood how copy-on-write works in Swift.<br class=""><br class="">Also, I'd like to know:<br class="">- besides capture lists, what are the correct ways to pass a copy-on-write value between threads?<br class="">- 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 ?<br class=""><br class="">Any help would be greatly appreciated.<br class="">Thanks.<br class=""><br class="">Note: I'm using Swift 4 with the latest Xcode version (9.2 (9C40b)) and the thread sanitizer enabled.<br class=""><br class="">_______________________________________________<br class="">swift-users mailing list<br class=""><a href="mailto:swift-users@swift.org" class="">swift-users@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-users<br class=""></div></div></blockquote></div><br class=""></div></body></html>