How to avoid deadlocking on finite channel

code as following will deadlock.

c = Channel(2)

t1 = Threads.@spawn begin
    while !trylock(c)
        yield()
        println("trylock for reading")
    end
    try
        put!(c, rand())
        put!(c, rand())
        put!(c, rand())
    catch e
        println(e)
    finally
        unlock(c)
    end
end

t2 = Threads.@spawn begin
    while !trylock(c)
        yield()
        println("trylock for reading")
    end
    try
        println("output:", take!(c))
    catch e
        println(e)
    finally
        unlock(c)
    end
end

how to solve it?
The programe should be not deadlocking! Who know why?

I don’t think you need to manually lock anything, just use put! and take! normally will be enough. If a thread tries to take when there are no elements it will just wait. If you put when you at capacity, again, it will just wait. It’s probably simplest to take this approach.

c = Channel(2)

t1 = Threads.@spawn begin
    for i in 1:3
        put!(c, rand())
    end
end

t2 = Threads.@spawn begin
    v = take!(c)
    println("output: $v")
end

wait(t1)
wait(t2)

Thanks!
But, why my code is not deadlocking, I locked it manually?

Because you lock the channel manually, and try to then put! elements inside? put! will internally lock the channel, you shouldn’t manually lock it, and it will wait to be able to lock.

Also, if your second thread manually locks c and then you try to take! and there are no elements it will hang forever as nothing can put anything into c as it is locked.

No, it is not that situation.

julia> c = Channel(2)
Channel{Any}(2) (empty)

julia> t1 = Threads.@spawn begin
           while !trylock(c)
               yield()
               println("trylock for reading")
           end
           try
               put!(c, rand())
               put!(c, rand())
               put!(c, rand())
           catch e
               println(e)
           finally
               unlock(c)
           end
       end
Task (runnable) @0x00007f18e088b990

julia> istaskdone(t1)
false

julia> Threads.@spawn begin
           @show trylock(c)
       end
Task (runnable) @0x00007f18e18cc8b0

julia> trylock(c) = true

Thanks again.
I see now. You are right.
I tried and find out that lock can be locked many times in one thread.
put!(c, "any") check the condition itself, and the lock status be reset to the right one at the same.

So, Channel is thread safe.

1 Like