There aren’t many resources on Condition Variables in Ruby, however most of them are wrong. Like ruby-doc, tutorial here or post here – all of them suffer with possible deadlock.
We could solve the problem by starting threads in given order and maybe putting some sleep in between to enforce synchronization. But that’s just postponing the real problem.
I rewrote the code into a classical producer-consumer problem:
require 'thread'
queue = []
mutex = Mutex.new
resource = ConditionVariable.new
threads = []
threads << Thread.new do
5.times do |i|
mutex.synchronize do
resource.wait(mutex)
value = queue.pop
print "consumed #{value}\n"
end
end
end
threads << Thread.new do
5.times do |i|
mutex.synchronize do
queue << i
print "#{i} produced\n"
resource.signal
end
sleep(1) #simulate expense
end
end
threads.each(&:join)
Sometimes you will get this (but not always):
0 produced
1 produced
consumed 0
2 produced
consumed 1
3 produced
consumed 2
4 produced
consumed 3
producer-consumer.rb:30:in `join': deadlock detected (fatal)
from producer-consumer.rb:30:in `each'
from producer-consumer.rb:30:in `<main>'
What is the correct solution?
This is more robust solution with multiple consumers and producers and usage of MonitorMixin,
MonitorMixinhas a specialConditionVariablewithwait_while()andwait_until()methods