It works best in Thin (EventMachine) or Rainbows! (Fibers).
If you are doing I/O you should use NeverBlock (http://www.espace.com.eg/neverblock/) they've even implemented a MySQL driver, the best part is they're 'drop-in' so you don't have to learn a new API, they just hide the async calls from you.
Fibers I think are closer to Erlang style than the event loop, and much more straight forward to program.
> Honest question: is there a purely event-driven I/O framework (no threads anywhere)?
You might look into a continuation-based framework. With continuations, you can do purely event-driven IO written in a direct style: the framework can grab the "callback" itself by snagging the continuation of its slow IO functions.
You still run into the same problems with blocking (non-IO, or non-framework) code taking up more than its fair share of time, however.
I see. My (wrong) understanding was that EM used a thread pool to do file I/O like Node.js. It appears to add the file descriptor to the event loop's watch list.
The one that a lot of people have gotten excited about recently is Node.js and that uses threads for file I/O (libeio).