Thread lock with async channel?

The @threads seems not working well with async channel. the following code blocks forever, is there something wrong?

using Base.Threads

c = Channel{Int}(1)

mutex = SpinLock()

@sync begin 
    @async begin 
#         for i in 1:10
        @threads for i in 1:10
            lock(mutex)
            push!(c, i)
            unlock(mutex)
        end 
    end
    
    @async begin 
        while true
            lock(mutex)
            i = take!(c)
            unlock(mutex)
            println(i)
        end
    end
end
close(c)
1 Like

Not answering your question, but I am curious if lock / unlock pattern is common, would a @locked macro like this be useful?

julia> using Base.Threads: lock, unlock

julia> macro locked(mutex, block)
           quote
               lock($mutex)
               value = $block
               unlock($mutex)
               value
           end |> esc
       end
@locked (macro with 1 method)

julia> using Base.Threads: SpinLock, @threads

julia> c = Channel{Int}(1)
Channel{Int64}(sz_max:1,sz_curr:0)

julia> m = SpinLock()
Base.Threads.TatasLock(Base.Threads.Atomic{Int64}(0))

julia> @threads for i in 1:10
           @locked m begin
               push!(c, i)
               take!(c) |> println
           end
       end
1
2
3
4
5
6
7
8
9
10

julia> close(c)

I think this is deadlocking, is there a way to check if a mutex is locked?

@async blocks are managed with Tasks. So are Channels. Tasks are currently implemented through the event-loop infrastructure of libuv, which is not thread-safe. Perhaps one of the developers can explain this strange design decision.

Even without threads, I think you need more logic, probably with an explicit yield, to avoid deadlock.

1 Like