Skip to content

Parallelism: Should wait(::Condition) take a Mutex to unlock/lock? #30026

Closed
@NHDaly

Description

I saw that Base provides a wait(::Condition) function, to add yourself to the Condition's queue:
https://docs.julialang.org/en/v1/stdlib/Distributed/#Base.wait

julia/base/event.jl

Lines 40 to 51 in d789231

function wait(c::Condition)
ct = current_task()
push!(c.waitq, ct)
try
return wait()
catch
filter!(x->x!==ct, c.waitq)
rethrow()
end
end

Unless I'm missing some other mechanism that exists for this, I think we also need a way to provide the Condition with a Mutex that it will unlock before sleeping, and re-lock upon awaking.

After the multithreading support is in and tasks can run in parallel, I think this is necessary in order to use Conditions for synchronization.

The pattern I'm describing is the Mesa Monitor, and the wikipedia article covers the required API nicely:
https://en.wikipedia.org/wiki/Monitor_(synchronization)#Condition_variables_2

We can either have the wait function take a mutex argument, as in the above Wikipedia example, or we could provide the mutex in the Condition constructor.

C++ takes the first approach: void wait( std::unique_lock<std::mutex>& lock )
https://en.cppreference.com/w/cpp/thread/condition_variable/wait

And Golang takes the second approach: func NewCond(l Locker) *Cond
https://golang.org/pkg/sync/#Cond.Wait
https://golang.org/pkg/sync/#NewCond

I'm not totally sure about the tradeoffs between those two, but I guess seeing as most of our Concurrency API is based on Go's, it might be good take the decision they made.


Here is an example of using the pattern I'm describing:

m = Mutex()  # global
cv = Condition()   # global

function task1()
  lock(m)
  while (!somepred())
    wait(cv, m)  # unlocks m; waits for signal; acquires m
  end
  # ... do stuff ...
  unlock(m)
end
function task2()
  lock(m)
  changepred()
  notify(cv)
  unlock(m)
end

I'm happy to open a PR for this as well, but I just wanted to check that I'm not missing something obvious before doing so! :)

Metadata

Assignees

No one assigned

    Labels

    multithreadingBase.Threads and related functionality

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions