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.