[swift-users] Data races with copy-on-write

Romain Jacquinot rjacquinot at me.com
Tue Dec 5 14:23:41 CST 2017

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> {
		defer { _lock.unlock() }
		return _myArray

	public func doSomethingThatMutatesArray() {

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 <jens at mooseyard.com> wrote:
>> On Dec 5, 2017, at 6:24 AM, Michel Fortin via swift-users <swift-users at swift.org <mailto:swift-users at swift.org>> 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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20171205/70f1cb02/attachment.html>

More information about the swift-users mailing list