Partially answering myself on issues raised in Edit2 & Edit3. I'd say it is not safe to use Array from different threads without synchronisation, even under those special circumstances of changing just the contents and not the size. Or even if any access to the array is "read" access. Here is why. The following implementation of Swift Array is not unimaginable:
enum LogAccess {
case count(result: Int)
case getter(index: Int, result: WeakHolder)
case setter(newElement: WeakHolder, oldElement: WeakHolder, index: Int)
// ... insert, delete, etc
}
struct Array<Element> {
var realCount: Int { ... }
func realGet(at index: Index) -> Element { ... }
mutating func realSet(_ element: Element, at index: Index) { ... }
#if SPECIAL_DEBUG_MODE
var logAccess: [LogAccess] = []
#endif
var count: Int {
let n = realCount
#if SPECIAL_DEBUG_MODE
// Edit: bug here. won't be able mutating logAccess from immutable "count".
logAccess.append(.count(result: n))
#endif
return n
}
subscript(_ index: Int, x: Int) -> Element {
get {
let result = realGet(at: index)
#if SPECIAL_DEBUG_MODE
// Edit: bug here. won't be able mutating logAccess from immutable "get".
logAccess.append(.getter(index: index, result: result))
#endif
return result
}
set {
let oldElement = realGet(at: index)
realSet(newValue, at: index)
#if SPECIAL_DEBUG_MODE
logAccess.append(.setter(newElement: newValue, oldElement: oldElement, index: index))
#endif
}
}
}
where SPECIAL_DEBUG_MODE is activated e.g. in Xcode diagnostics or as a compilation flag.
This implementation doesn't violate any Array guarantees (e.g. O(1) operations remain so) but it is obvious if you try to call even array.count (or array."anything else") array[x] = y on the same array from two different threads the underlying unsynchronised access to logAccess will crash, trash memory, or misbehave in other ways. To me this is a proof that any mutable Array call (even count) must be synchronised (via mutex, or serial dispatch queue, etc) if array is used in several threads , even if that use is "all readers, no writers". Sounds weird, I know. Would like to be proven wrong on this one, perhaps I'm missing some language guarantees, or "unwritten but obvious rules" here.
Usually @eskimo is very good with these matters, so mentioning him just in case he missed this thread... ;-)
PS. I changed LogAccess-s "Any" to "WeakHolder" (whatever that is) to not introduce strong references to array elements.
PS2. Correcting above: "read access" seems to be fine, as it won't be possible mutating logAccess variable from immutable methods.. unless logAccess is not an instance variable but some static / global variable - but in that case its use will have to be protected internally as it will be used from different arrays (from different threads).