
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.