Swift Arrays are not Threadsafe

Swift arrays are not necessarily contiguous blocks of memory (although they may be in some cases so some code may work in some cases but not in others). Some operations may trigger them to be converted to a contiguous block of memory and I believe that is what can particularly cause issues when there is access from multiple threads.

[Update - This post is more popular than I expected (thanks Reddit). I've added some additional notes at the bottom. Also the previous post about optimisation is probably more interesting and valuable.]

Even the code as described here may not work (and I haven't tested at all) when there are multiple reads or writes that access the same elements of the array. All the code I used was either reading from a constant array (safe from multiple threads/queues) or writing into non-overlapping ranges with no reads occurring (only seems safe with the technique described here).

.withUnsafeMutableBufferPointer

If you need to mutate an array in multiple threads (see previous posts on Swift optimisation) you can do this by instead using an UnsafeMutableBufferPointer created from the array with the method .withUnsafeMutableBufferPointer and usable within the block passed in. When you call this it ensures that a contiguous buffer is created if it doesn't already exist. This can be used in different queues provided that you use some synchronisation to ensure that all those blocks have completed before you exit the block.

In the optimisation work I did on GrayScott it originally worked without this precaution and I'm not sure whether I was just lucky in some way or if the implementation changed in Xcode 6.1 that stopped it working in general.

The key commit that fixed the image corruption issues that I was seeing can be seen here:
As you can see in that change the two inout (really just out) arrays in the partial solver are changed to UnsafeMutableBufferPointers but the actual code within is unchanged. There is the new pair of nested withUnsafeMutableBuffer blocks which are not closed until after the dispatch_group_wait call that signals all the dispatched partial solvers are complete and are no longer accessing the UnsafeMutableBufferPointers.

I might look into this further in future but I wanted to get this information out quickly in the hope that people find it useful.

Links

Teton Technical covers some of the Swift pointer wrappers.

Update - Additional Notes

I just wanted to note that this post wasn't intended as a complaint about Swift's arrays (I did complain about the pre-beta 3 semantics but they fixed that) but just a statement about how they do behave.

I had been optimising some code as an experiment and it had been working writing in parallel to different areas of the array although it was probably a programming error. With Swift 1.1 (Xcode 6.1) I was getting errors that I have managed to work around by using the capabilities Swift provide to work on the array as a raw buffer within a block.

It should be safe to share read access to an array in Swift and note that arrays are value types so unless wrapped in an object (of a class type) or explicitly passed by reference as an inout parameter they should be safe to use across threads as they will really be copy-on-write.

I wasn't expecting as much traffic for the post which was was just a quick one I dashed out late at night to explain how I had worked around an issue so I probably didn't cover these areas as thoroughly as I should have done.