From: Mark Volkmann Date: 2006-01-11T01:06:26+09:00 Subject: flawed use of ConditionVariable Example code I've seen that uses a ConditionVariable seems to have a timing flaw. For example, in Pickaxe 2, page 147 in the Player/Customer example, the player thread does plays_pending.wait_while { playlist.empty? } This assumes that ok_to_shutdown gets set and detected in the previous line before wait_while is called. If the timing works out such that ok_to_shutdown doesn't get set to true until after wait_while is called, I believe it will hang forever. Isn't it the case that the customer thread could finish and the player thread could reach the call to wait_while before ok_to_shutdown gets set to true? I think what needs to happen is what I show in the following example. Note how the wait_while checks whether the producer thread is alive and how I do a condition.signal at the bottom when I know the producer thread has terminated ... which causes wait_while to evaluate the condition again. The call to sleep at the end of the producer thread is critical to illustrating the problem. If I take that out then I can get lucky with the timing and don't need to test whether the producer thread is alive in wait_while. Am I off my rocker? ;-) require 'monitor' #---------------------------------------------------------------------- class Widget attr_accessor :id def initialize(id) self.id = id end def to_s id end end #---------------------------------------------------------------------- bin = [] bin.extend MonitorMixin condition = bin.new_cond # type is MonitorMixin::ConditionVariable #---------------------------------------------------------------------- producer = Thread.new do widget_id = 0 5.times do widget_id += 1 widget = Widget.new(widget_id) puts "produced #{widget.to_s}" bin.synchronize do bin << widget condition.signal # there is a widget to consume end end # Delay termination of producer thread # to demonstrate need for another condition.signal. sleep 1 end #---------------------------------------------------------------------- consumer = Thread.new do finished = false until (finished) do bin.synchronize do finished = (not producer.alive?) and bin.empty? break if finished # The condition will be tested once, # and then again every time condition.signal is called. condition.wait_while { producer.alive? and bin.empty? } widget = bin.shift # removes first Widget from bin puts "consumed #{widget.to_s}" if widget != nil end end end #---------------------------------------------------------------------- producer.join bin.synchronize do # Make consumer retest condition after producer terminates. condition.signal end consumer.join -- R. Mark Volkmann Partner, Object Computing, Inc.