------art_25164_3799979.1160057414737
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

On 10/5/06, Serge Kruppa <serge.kruppa / simitel.com> wrote:
>
>  An issue we have is that all our code must run in an embedded Ruby
> environment (Active Ruby), packaged as a traditional Windows application
> (MFC). Ruby is the hidden jewel inside a fairly standard Windows program.
> If
> I understood your suggestion correctly, the idea would be to layer an
> EventMachine on top of our C code already packaged as a Ruby extension.
> The
> EM would act as a UDP event-driven middleware, passing function requests
> and
> responses back and forth between our main Ruby program and the C code? How
> do you suggest we interface the EM with the main Ruby program?
>
> Our Ruby program structure is not event driven unfortunately. I did
> convert
> during the night the C native function that invokes the Ruby callbacks to
> a
> simple UDP message send with a specially formatted text string containing
> the parameters. The EM receives these UDP callback notifications very
> effectively and passes them on to actual Ruby Proc objects for further
> processing.



The best way to structure an event-driven program (or a traditional program
that responds to events) depends on that factors that influence the
program's control flow. It's always easiest if you can let EM run your
program, meaning that all of your program's work is done by responding to
timers, signals, or exogenous events. However, your program may have a GUI
or very-long-running batch jobs that don't like to be interrupted. In these
cases, you can run EM on its own Ruby thread. Bear in mind that all of the
code you write that responds to external events controlled by EM will then
be executed on the separate EM thread, and you'll need to synchronize this
code (using Ruby sync objects) with your main line. But EM works seamlessly
with Ruby threads, so none of this is too difficult. Another alternative is
to separate your program into multiple processes, with the traditional logic
in one piece and the event-driven stuff in a different one.


However, taking the example of my unit test suite, I'm using a simple fall
> through logic of test cases executed in logical sequence. I found it
> impossible to naively insert the EM into my test suite (I'm ashamed to
> admit
> that I tried to run the EM within a Thread, which resulted in the EM
> catching only the first UDP callback and missing all the subsequent ones).


It's definitely messy to unit-test event-driven code because test suites by
nature want to manage their own sequential logic. What I generally do is to
call EventMachine#run inside each test function in a test suite, and have
one of my event handlers call EventMachine#stop as soon as the interesting
code has executed. This works quite well. If you're afraid that a bug in
your code on any given test run will prevent #stop from being called (and
hang your test run), then just add an EM timer that will call stop after one
second and then throw an exception that your test suite will respond to.

Regarding your question about the execution profile of our C code, it is
> mostly RPC style: one computer sends a request to another and blocks on a
> PThread condition variable until a response is received from the other end
> or a timeout occurs. We do have database operations as well, protected by
> a
> series of PThread mutexes.
>
> I have been looking for more information about the EM deferred execution
> pattern but did not find any straightforward description. Any pointers to
> suitable Web pages would be most appreciated :)


Now I see why you typically have 50 threads, it's because that's how many
pending requests you have in flight at any given time. Doing this with UDP
can be an interesting challenge for a couple of different reasons, but I've
found that there generally is a simple and graceful way to build these. This
application pattern is fundamentally event-driven so I think you'll find
that a tool like EM will work with you, whereas a threaded approach will
generally work against you. If you want design hints, feel free to write me
privately and give me some more detail on how these "RPCs" work.

About deferred operations: there are two kinds of these, EventMachine#defer
and the Deferrable module. The first is straightforward and was designed to
deal with cases that block your program locally, the classic example being a
database call using a DB library that is not event-ready. EventMachine#defer
manages an internal pool of Ruby threads. Read the rdoc for #defer, there is
complete information there and sample code.

I've found that the Deferrable pattern is rather hard for people to
understand (and it's not well documented yet). It's very much like the
Deferred objects in Python's Twisted. It basically allows you to kick off an
event-driven operation that may (or may not) take some time to execute, but
bundle in the processing that will be executed on success or failure. If can
give ridiculously high performance for things like accessing Web Services or
other HTTP servers if you use it right. Examples: I've used it to open 1000
simultaneous connections to servers in a LAN in about two-tenths of a
second; 500 completed HTTP GETs to Internet hosts in less than a second, and
so on.

------art_25164_3799979.1160057414737--