Skip to content

Instantly share code, notes, and snippets.

@mperham
Created September 20, 2011 02:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mperham/1228184 to your computer and use it in GitHub Desktop.
Save mperham/1228184 to your computer and use it in GitHub Desktop.
ConditionVariables can wakeup threads that are no longer waiting on them
require 'thread'
# This shows how the main thread can be woken up by
# a condition variable that it is no longer waiting on.
# When waking up via timeout, the main thread is not removed
# from the waitlist and so gets a spurious wakeup from cond1
# when it is no longer waiting on that variable.
#
# Result should be '2' but is actually '1' on Ruby 1.9.2.
# This code works on JRuby 1.6.4.
result = 'none'
puts Time.now.to_f
mutex1 = Mutex.new
cond1 = ConditionVariable.new
t1 = Thread.new do
sleep 0.5
result = '1'
# this will signal the main thread when it is waiting on cond2
cond1.signal
end
mutex1.synchronize do
cond1.wait(mutex1, 0.3)
end
puts Time.now.to_f
mutex2 = Mutex.new
cond2 = ConditionVariable.new
t2 = Thread.new do
sleep 0.5
result = '2'
cond2.signal
end
mutex2.synchronize do
cond2.wait(mutex2, 1)
end
puts Time.now.to_f
puts result
raise ArgumentError, 'result != 2' if result != '2'
@ryanlecompte
Copy link

Here's the implementation of ConditionVariable#wait and ConditionVariable#signal for MRI 1.9.2. It looks like in #wait, if the timeout occurs, the Thread.current isn't removed from the @waiters array. So later on, in #signal, the old Thread.current instance is still in the @waiters array and gets notified even though it timed out!

  #
  # Releases the lock held in +mutex+ and waits; reacquires the lock on wakeup.
  #
  # If +timeout+ is given, this method returns after +timeout+ seconds passed,
  # even if no other thread doesn't signal.
  #
  def wait(mutex, timeout=nil)
    begin
      # TODO: mutex should not be used
      @waiters_mutex.synchronize do
        @waiters.push(Thread.current)
      end
      mutex.sleep timeout
    end
    self
  end

  #
  # Wakes up the first thread in line waiting for this lock.
  #
  def signal
    begin
      t = @waiters_mutex.synchronize { @waiters.shift }
      t.run if t
    rescue ThreadError
      retry
    end
    self
  end

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment