xxx_poll callbacks and multiple event loops

Marc Lehmann schmorp at
Tue Nov 8 18:31:41 CET 2011

On Sun, Nov 06, 2011 at 01:50:04PM +0000, Paddy Byers <paddy.byers at> wrote:
> I mean multiple libev event loops, each running in a separate thread. The
> use case is to be able to run multiple instances of node.js (effectively
> multiple isolates) in a single process.

You are reinventing processes - I can only suggest that this is the wrong
design, it's slow.

The reason it is slow is that it has to emulate the MMU in software (since
in threads, memory is shared, you have to separate namespaces by e.g.
having a "root namespace pointer" passing around or in a thread-local

So, if you want isolated instances, use processes, and use hardrwae
support for that. node.js will scale much better that way - you could even
run isolated instances on different hosts with relatvie ease, unless you
do some memory sharing.

Or, as corollary, it looks to me as if you are trying to implement a bad
design, head-through-wall, because your problems will go away once you
run isolated instances in their own process, which is what your OS is
optimised for, instead of emulating cpu hardwrae in software.

> The per-request processing that occurs on pending completions - ie the
> processing triggered by want_poll - but be done in the thread associated
> with the original request.

Your proposed change doesn't achieve that, and is not a step into that
direction. The only way to achieve that is to have completely separate
queues for each thread, and separate poll calls for each such queue.

And while it is implementable, given your goal, it's simply the wrong
solution to your problem, because it is a direct result of forcing the
wrong model on node.js (and thus all it's components).

> All of this should be achievable using a single instance of libeio - in
> fact my understanding is that there can only be a single instance of libeio
> in a single process; there is only one call to eio_init() and all of the
> state initialised there is process-wide.

Yes, that's how it is at the moment.

It would be theoretically possible to change that, but so far, there
has been no good reason to do so: again, your reason is valid, but only
because node.js implements the wrong design and thus it would be more
convenient to also implement the wrong design in libeio.

> One could in theory pass the eio_req itself to the callback, but I am not
> > sure this will lead anywhere helpful - this will still not make requests
> > attached to an event loop (whoever calls poll handles the requests).
> That would be good enough for my use-case - to be able to get the eio_req
> back, because I could get back to the event loop from there.

And then? The next call the eio_poll will still process requests from
other threads as well.

> Is there a way I can do this in the want_poll callback?

Again, head-through-wall, you could do that, but it would be wrong to do so.

If node.js really insists on emulating processes in software, then a
simpler way would be to have one thread doing eio_poll and signalling the
other threads.

i.e. simply replace the callback by some callback that does something like:

  my_eio_callback (eio_req *req)
    struct ev_loop *l = ((my_eio_req *)req)->notify_loop;
    ev_async *w       = ((my_eio_req *)req)->notify_async;
    my_queue *q       = ((my_eio_req *)req)->notify_queue;

    my_queue_enqueue (q, req);
    ev_async_send (l, w);

And then the async callback would unqueue requests from the queue and
call, e.g. ((my_eio_req *)req)->node_cb (...) or better yet handle each
request separately.

That wouldn't be superefficient, but since node.js's goal isn't to be
efficient (otherwise it would use processes :) I guess thats ok. It's also
no so bad on single core systems, which is after all what threads are
designed for.

On multi-core systems, it depends on the schedule - linux is quite bad
here and often schedules threads on different cpus, but I guess if node.js
uses threads instead of processes thats the least performance issue there
is, giving all the extra overhead of threads on multicore cpus.

> The ideal thing would be if eio could call my want_poll exactly once for
> each distinct poll_data. I'm not sure yet if I can achieve that behaviour
> from outside.

No, that would be quite slow. What you are asking here is to make libeio
inefficient for everybody because node.js choose to implement an inefficient
software-process model.

You can do what you want with the existing API without forcing slowness on
everybody else by having a demultiplexer thread that calls eio_poll (and
it could even efficiently use e.g. a condition variable and wait for that)
and then sends each request to each libev.

                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_    
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      schmorp at
      -=====/_/_//_/\_,_/ /_/\_\

More information about the libev mailing list