I was reading through @Dan’s answer here to lock a struct using a spinlock. Lets say we are in a situation where we have a struct that in its entirety needs to be updated. Could we use something like:
struct Entry
var::UInt32
end
function test()
n = 10
lock_vector = [Base.Threads.SpinLock() for i in 1:n]
data = [Entry(0) for i in 1:n]
@Threads.threads for i in 1:100
j = rand(1:n)
openm lock(lock_vector[j])
data[j] = Entry(Threads.threadid())
end
end
end
test()
It seems quite intuitive to me to have a layer locking data. Especially in cases where variables depend on each other. For example:
function test()
n = 10
lock_vector = [Base.Threads.SpinLock() for i in 1:n]
data = [Entry(1) for i in 1:n]
@Threads.threads for i in 1:10
j = rand(1:n)
# We can now easily lock a range
target_range = 1:j
lock.(view(lock_vector, target_range))
println(islocked.(lock_vector))
prefix_sum = sum(e.var for e in data[target_range])
data[j] = Entry(prefix_sum)
unlock.(view(lock_vector, target_range))
end
println(data)
end
Showing the locks:
Bool[1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
Bool[1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
Bool[1, 1, 1, 1, 1, 1, 0, 0, 0, 0]
Bool[1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
Bool[1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
Bool[1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
Bool[1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
Bool[1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
Bool[1, 1, 1, 1, 0, 0, 0, 0, 0, 0]
Bool[1, 1, 1, 1, 1, 1, 1, 0, 0, 0]
I know ideally locking should be avoided, but lets say it cannot be avoided. Would this approach have “dangerous” side effects? (for example, since not the actual data is locked could the compiler rearrange the code rendering this useless)