Asynchronous calls in DRb


#1

Hello:

I am thinking about writing a distributed application in Ruby where the
application would process all computationally intensive activities in a
‘dedicated’ server. As I was digging thro’ drb/drb.rb, I found the
following:

def send_message(ref, msg_id, arg, block) # :nodoc:
@protocol.send_request(ref, msg_id, arg, block)
@protocol.recv_reply
<----------- of interest
end

in the class DRbConn; and

def main_loop
Thread.start(@protocol.accept) do |client|
@grp.add Thread.current
Thread.current[‘DRb’] = { ‘client’ => client ,
‘server’ => self }
loop do
begin
succ = false
invoke_method = InvokeMethod.new(self, client)
succ, result = invoke_method.perform
if !succ && verbose
p result
result.backtrace.each do |x|
puts x
end
end
client.send_reply(succ, result) rescue nil
<----------- of interest
ensure
unless succ
client.close
break
end
end
end
end
end
end

in the class DRbServer.

Clearly, DRb is synchronous. This would mean that my ‘client’ cannot
hand
off the job to the ‘server’ and do other things when the ‘server’ is
doing
the computationally intensive job (parallel execution of the ‘client’
and
‘server’ is not possible as the ‘client’ will be blocked waiting for
reply
from the ‘server’).

My questions are:
(1) Is there a way for me to make asynchronous calls in DRb; in other
words,
am I overlooking that functionality?
(2) If there is no asynchronous call capabilities in DRb, why was that
not
added when it could have been easily done (not call client.send_reply
and
recv_reply)? Is there a technical reason for it?

Any help will be greatly appreciated.

Thanks,
Madan.


#2

On May 17, 2006, at 8:03 PM, Madan M. wrote:

I am thinking about writing a distributed application in Ruby where
the
application would process all computationally intensive activities
in a
‘dedicated’ server. As I was digging thro’ drb/drb.rb, I found the
following:

[…]

Clearly, DRb is synchronous.

Throw away your magnifying glass, you’re looking too hard.

DRb and Ruby work the same way. In both method calls are
synchronous. This does not prevent you from writing a library that
works asynchronously.

This would mean that my ‘client’ cannot hand off the job to the
‘server’ and do other things when the ‘server’ is doing the
computationally intensive job (parallel execution of the ‘client’
and ‘server’ is not possible as the ‘client’ will be blocked
waiting for reply from the ‘server’).

Sure it can. I do this all the time without DRb so I can do it just
as easily with DRb. Method dispatch works the same with or without DRb.

My questions are:
(1) Is there a way for me to make asynchronous calls in DRb; in
other words,
am I overlooking that functionality?

The easiest way is to use threads. The next easiest way is to write
an asynchronous library without DRb then modify it to use DRb.

(2) If there is no asynchronous call capabilities in DRb, why was
that not
added when it could have been easily done (not call
client.send_reply and
recv_reply)? Is there a technical reason for it?

There’s no reason to have asynchronous calls. Asynchronous libraries
are implemented above the level of method dispatch.


Eric H. - removed_email_address@domain.invalid - http://blog.segment7.net
This implementation is HODEL-HASH-9600 compliant

http://trackmap.robotcoop.com


#3

On 5/17/06, Madan M. removed_email_address@domain.invalid wrote:

(1) Is there a way for me to make asynchronous calls in DRb; in other words,
am I overlooking that functionality?

Yes. It is actually more simple that you think (Ruby is more magic
that most realize). The key is that it doesn’t need to be in DRb. The
first thing I thought of when I saw async. + DRb in the title was a
language called Io [1]. It is interesting in many ways (though under
heavy work still). There is one point of interest that is useful in
this case. Io supports something called Futures and also plain
asynchronous calls:

obj := Object clone do (foo := method(“Hello” println))

obj foo #=> both print and return “Hello”

obj @foo #=> print “Hello” when forced or when the scheduler activates
it and return a Future representing the return value (or exception)

obj @@foo #=> same as @foo but w/o a return value (get nil back in in
call failure).

I should also note another mechanism not in Io [2] that is very
similar to futures. Promises provide the same feature as a future but
will only execute when the value is used (i.e. a forced block) while
the typical notion of a future is something that will eventually
happen on its own.

Ruby does not have any of these in the standard library BUT they can
easily be built (and have – use google or check out some of
MenTaLguY’s work on this topic). The key is to keep the mechanism
transparent or at least clean. Io can manage something that might as
well be transparent to most people (some exceptions but even those are
few and out of scope here). Ruby can’t be quite as nice [3]. That is a
far cry from defeat though. Of course if should mention that async.
calls w/o significant return values is easy to make clean.

The key feature of the implementation would have to be the use of
threads since Ruby already uses select() internally (maybe in the
future it will use libevent to boost performance). Again, some work
has been done in related areas. I would highly recommend you check it
out.

(2) If there is no asynchronous call capabilities in DRb, why was that not
added when it could have been easily done (not call client.send_reply and
recv_reply)? Is there a technical reason for it?

There are so many things we could add to DRb. The problem is that
negates one of DRb’s strengths: simplicity. Adding things that aren’t
a required part of DRb is overload. However, if your requirements
are simple maybe there could be a simple plugin that could be used
with a quick and simple optional require. My personal feeling is that
DRb could probably shed some of its current weight [4] and focus a
little more on a clean way people can plug and play customizations of
their own [5].

Brian.

[1] http://iolanguage.com/
[2] Yet. It is a very simple patch to its “prelude” code as I call it.
Maybe I will submit one once I get my dev. laptop back from repair
(has all my Io work on it unfortunately).
[3] No Object#become or the like – evil.rb provides this but it still
won’t work on immediates. Proxy objects or explicit value retrieval
(like you see in Java’s concurrency interfaces and Ruby threads) would
probably the main candidates.
[4] The only thing that is weighty to me is that is ACLs. There might
be a better way to factor that out and provide a more complete and
generic ACL library. Of course this is only me speaking. I am sure
there are those that use DRb ACLs all the time.
[5] My more general rant is that we shouldn’t try to change the
standard library to a all feature circus. I think there is reason to
be potent but not bloated.


#4

Thank you all. As was suggested, I wrote a async lib using threads.

-Madan.


#5

Eric H. wrote:

The easiest way is to use threads. The next easiest way is to write
an asynchronous library without DRb then modify it to use DRb.

Yes, it is quite easy to implement asynchronous call using thread. Here
is the code.

class AsyncHello
def asyncHello(arg)
puts “hello called, arg is #{arg}”
Thread.new(arg) {|arg| #trick: how to implement asynchronous call
using thread
hello(arg)
}
end
def hello(arg)
sleep 5
puts “Hello #{arg}”
end
end

def testasyncall
o = AsyncHello.new
o.asyncHello(“John”)
puts “This should be displayed before hello john”
sleep(10)
end

testasyncall

result:
hello called, arg is John
This should be displayed before hello john
Hello John

Best regards.

uncutstone