# Using a lock array to prevent multi thread corruption

**URL:** https://discourse.julialang.org/t/using-a-lock-array-to-prevent-multi-thread-corruption/115485
**Category:** Performance
**Tags:** question
**Created:** [June 11, 2024, 3:45pm UTC](https://discourse.julialang.org/t/using-a-lock-array-to-prevent-multi-thread-corruption/115485 "2024-06-11T15:45:18Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![CodeGodz](https://avatars.discourse-cdn.com/v4/letter/c/aeb1de/32.png) [@CodeGodz](https://discourse.julialang.org/u/CodeGodz)
#### Post date: [June 11, 2024, 3:45pm UTC](https://discourse.julialang.org/t/using-a-lock-array-to-prevent-multi-thread-corruption/115485/1 "2024-06-11T15:45:18Z")

</div>

I was reading through @Dan’s answer [here](https://discourse.julialang.org/t/is-it-possibly-to-do-atomic-update-on-an-array-element/112113/4) 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:

```julia
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:

```julia
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:

```julia
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)

---

<div class="post-metadata">

### Author: ![foobar\_lv2](https://avatars.discourse-cdn.com/v4/letter/f/ee59a6/32.png) [@foobar\_lv2](https://discourse.julialang.org/u/foobar_lv2)
#### Post date: [June 11, 2024, 10:05pm UTC](https://discourse.julialang.org/t/using-a-lock-array-to-prevent-multi-thread-corruption/115485/2 "2024-06-11T22:05:47Z")

</div>

1. Are you sure about spinlock?

```julia
help?> Base.Threads.SpinLock
  SpinLock()

  Create a non-reentrant, test-and-test-and-set spin lock. Recursive use will
  result in a deadlock. This kind of lock should only be used around code that
  takes little time to execute and does not block (e.g. perform I/O). In
  general, ReentrantLock should be used instead.

```

This is no joke. If the critical section is long then this can blow up in your face.  
2. Beware of deadlock! For example the following can deadlock:

```julia
Threads.@threads for i=1:10
j = rand(1:n)
k = rand(1:n)
j == k && continue
locks = lock_vector[[j,k]]
lock.(locks)
...

```

In your specific example you only need to take the first lock (since every range starts at 1). You avoid deadlock by making sure that you always take the locks in the same order, from lowest to highest.

---

<div class="post-metadata">

### Author: ![CodeGodz](https://avatars.discourse-cdn.com/v4/letter/c/aeb1de/32.png) [@CodeGodz](https://discourse.julialang.org/u/CodeGodz)
#### Post date: [June 11, 2024, 10:38pm UTC](https://discourse.julialang.org/t/using-a-lock-array-to-prevent-multi-thread-corruption/115485/3 "2024-06-11T22:38:15Z")

</div>

Hey @foobar_lv2 thanks for the heads up. The spinlock performance seems fine for my usecase (will compare it to reentrant). Is there any reference for “takes little time” we talk ns I suppose?

> avoid deadlock by making sure that you always take the locks in the same order

Aah yeah I always take my ranges from low to high, and then lock. (not the best example with prefix sum, suffix sum would be better 😅)

* * *

Is there actually a way to check if a thread is waiting in Julia?
