Cool. I'd like to see an example of how to use asynchronous messages. Also check the the latest version of BlankSlate? bundled with my XML builder. The vanila version has a bug in that it is susceptable to methods added to Kernel or Object. Thanks. -- JimWeirich?
Silly me, no example. Here's a simple one:
require 'async'
class Show
def show(it)
puts "<<#{it.inspect}>>"
end
end
class Foo
def test(n)
n.times do
yield
Thread.pass
end
end
def test2(n)
sleep n
n
end
end
one = Foo.new
two = Foo.new
disp = Show.new
one.async(:test, 5) {puts "Hello"}
one.async(:test, 5) {puts "Goodbye"}
two.async(:test, 5) {puts "Hello2"}
one.async(:test2, 5).aresult_to(disp, :show)
one.async(:test2, 2).aresult_to(disp, :show)
two.async(:test2, 3).aresult_to(disp, :show)
__END__
Produces:
Hello Hello Hello Hello2 Hello Hello2 Hello Hello2 Hello2 Hello2 Goodbye Goodbye Goodbye Goodbye Goodbye <<3>> <<5>> <<2>>
I hadn't considered methods dynamically added to Kernel or Object after Async was defined ... taking a cue from your builder project I think this addition to Async might do it:
class Async
# prevent dynamic ancestor method additions
self.ancestors.each do |mod|
next if self == mod
class << mod
alias :old_method_added :method_added
def method_added(id)
old_method_added(id)
return unless self == Kernel or self == Object
Async.hide(id)
end
end
end
def Async.hide(id)
undef_method(id)
end
end
require 'thread'
require 'evil'
Async = Class.new(KernellessObject) do
def init(&blk)
@th = Thread.new(&blk)
@th.abort_on_exception = true
Kernel.at_exit{@th.join}
end
def method_missing(meth,*args, &blk)
__getobj__.__send__(meth,*args, &blk)
end
def __getobj__
@obj ||= @th.value
end
# control/status messages
def awake
@th.wakeup
self
end
def await
@th.join
end
def aready?
! @th.alive?
end
def aresult_to(obj,meth)
thr = Async.new
thr.init {obj.send(meth,__getobj__)}
thr
end
end
class Object
def async(msg,*args,&blk)
@async_queue ||= Queue.new
fut = Async.new
fut.init {Thread.stop;self.send(msg,*args,&blk)}
@async_queue << fut
@async_thread ||= Thread.new do
loop{thr = @async_queue.pop.awake.await;Thread.pass}
end
fut
end
end
__END__
Of course, evil.rb defines more than just a KernellessObject? --- more than we might want. (andrew)