Ruby Wait / Notify / NotifyAll

ruby
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.

comments powered by Disqus
by Joe Kinsella