------ 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--