Ok, let's assume a simple case:
class C {
var node: Node
init() {
// setup a node of several edges
}
}
let c = C()
now you have two threads each doing something like this:
c.node.edges[0].visitCount += 1
and you want that increment to be atomic.
Note that as written this is pretty much equivalent to:
var edge = c.node.edges[0]
edge.visitCount += 1 // we want this atomic
c.node.edges[0] = edge
which is not atomic, as a sequence as obviously this could happen:
Thread1
var edge = c.node.edges[0]
edge.visitCount += 1 // visitCount changed from 0 to 1
Thread2
var edge = c.node.edges[0]
edge.visitCount += 1 // visitCount changed from 0 to 1
c.node.edges[0] = edge // visit count stored is 1
Thread1
c.node.edges[0] = edge // visit count stored is STILL 1
Now, if you do have a reference...
let edge = c.node.edges[0]
let referenceObject = edge.visitCount
referenceObject.value += 1 // atomically somehow
that indeed would save you from having the whole sequence atomic... But, indeed you'd have to use reference objects (like ManagedAtomic) for the edges' content. It would indeed be quite heavy memory wise for a large number of nodes / edges.
I am not sure this is the best approach though. Maybe a simple lock is better in this case:
lock.underLock {
var edge = c.node.edges[0]
edge.visitCount += 1 // visitCount changed from 0 to 1
c.node.edges[0] = edge // visit count stored is 1
}
Edit: another option is to make Edge a class: then its fields would have stable memory addresses and you could do atomic operations on them a-la std::atomic style.
let edge = c.node.edges[0]
edge.visitCount += 1 // use atomic operation here instead
// job done
Bear in mind that class instances do have a space and time overhead: about 16-20 bytes or so of space and ARC retain count management of time.
I'd love to see what others think. cc @dnadoba