Ruby Wait / Notify / NotifyAll

While Ruby is one of the most productive and expressive languages in which I have programmed, I’ve been wondering lately whether the existence of the GIL has stunted its growth for concurrent programming. Instead of building concurrency into the language, most of the focus in the community has been around working within its limitations and scaling through processes. For example, there is no equivalent in Ruby of the Java concurrency package; the language supports a mutex but not a semaphore (isn’t a mutex just a semaphore with a count of 1?); and there is no construct for inter-thread signaling.

To workaround some of these limitations, I recently adopted JRuby as a means to gain access to the expressiveness of Java for distributed programming. But where possible, I have been trying to maintain MRI compatibility. Below is a simple implementation of the Java wait/notify pattern, which I've always thought was an elegant implementation of inter-thread signaling. Feedback is welcome - especially around the use of Thread.exclusive to avoid concurrency issues. Maybe someday we could push a similar extension back into the base Ruby language. ;)

  
# Old school Java wait/notify  
  
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'  
  # Use Java wait/notify/notifyAll  
  require "java"  
  
  class Object  
  
    def wait  
      create_monitor unless @monitor  
      @monitor.synchronized {  
        @monitor.wait  
      }  
    end  
  
    def notify  
      create_monitor unless @monitor  
      @monitor.synchronized {  
        @monitor.notify  
      }  
    end  
  
    def notify_all  
      create_monitor unless @monitor  
      @monitor.synchronized {  
        @monitor.notify_all  
      }  
    end  
  
    protected  
  
    def create_monitor  
      Thread.exclusive {  
        # If the monitor was created by another thread, just exit  
        return if @monitor  
        @monitor = java.lang.Object.new  
      }  
    end  
  
  end  
  
else  
  # Implement an equivalent in MRI  
  
  class Object  
    def wait  
      create_monitor unless @monitor  
      @monitor.synchronize {  
        @waiting_threads << Thread.current  
      }  
      # Warning: Due to lack of language support in Ruby, the thead stop is outside synchronized block  
      Thread.stop  
    end  
  
    def notify  
      create_monitor unless @monitor  
      if @monitor and @waiting_threads  
        @monitor.synchronize {  
          @waiting_threads.delete_at(0).run unless @waiting_threads.empty?  
        }  
      end  
    end  
  
    def notify_all  
      create_monitor unless @monitor  
      if @monitor and @waiting_threads  
        @monitor.synchronize {  
          @waiting_threads.each {|thread| thread.run}  
          @waiting_threads = []  
        }  
      end  
    end  
  
    protected  
  
    def create_monitor  
      Thread.exclusive {  
        # If the monitor was created by another thread, just exit  
        return if @monitor  
        @waiting_threads = [] unless @waiting_threads  
        @monitor = Mutex.new unless @monitor_mutex  
      }  
    end  
  
  end  
  
end  
  

A tip of my hat to my former professor and advisor, Doug Lea, who wrote the original java.util.concurrent package.