Home  |  Linux  | Mysql  | PHP  | XML
From:Rocco Caputo Date:Mon Apr 28 13:51:19 2008
Subject:Re: benchmarking various event loops with and without anyevent
On Apr 28, 2008, at 06:24, Marc Lehmann wrote:
> [most important points first]
>
>> In your case, I would create a single persistent POE::Session  
>> instance
>> that serviced all the watchers.
>
> I would, too, but I cannot find a way to do that with POE: sessions
> without active watchers will be destroyed, forcing the session to have
> active resources will make the program never-ending.
>
> I already told you that I tried this approach, and why I couldn't  
> get it
> working.

I might use something like an explicit reference count to keep the  
session alive.  I would create a proxy object that, when DESTROYed,  
would post a shutdown message to the session.  Or if AnyEvent knows  
when a program is to exit, I would have it explicitly shutdown the  
session as part of its finalization.

The shutdown handler would remove the explicit reference count,  
allowing the session to stop.

It's similar to the technique you use in AnyEvent::Impl::POE:

sub DESTROY {
    POE::Kernel->post (${${$_[0]}}, "stop");
}

Except it would be done once at the very end, rather than for each  
event watcher.

Perhaps this isn't necessary.  You're not using run(), so technically  
you're free to go at any time.  If your program must exit while  
watchers are active, then you could force the issue by sending a  
global UIDESTROY signal (designed to tell POE when it must  
unconditionally stop), and calling run():

$poe_kernel->signal($poe_kernel, "UIDESTROY");
$poe_kernel->run();

On the other hand, your AnyEvent::Impl::POE proxy objects could also  
hold references to the singleton session, and if they release those  
references when they clean up their POE::Kernel resources, the session  
should be "empty" by the time they all destruct.  In that case, the  
UIDESTROY signal should not be needed.  run() will return after  
removing the "empty" session.

In short, your AnyEvent::Impl::POE objects would be of the form:

sub new {
   # (AnyEvent::Impl::POE setup goes here)
   # set up the POE::Kernel watcher
   $poe_kernel->something(something);
   # make a note of the watcher in this object
   $self->{something} = $record_of_the_poe_watcher;
   return $self;
}

sub DESTROY {
   my $self = shift;
   # ... release the POE::Kernel watcher
   $poe_kernel->something($self->{something}, something);
}

If you expect the user to be creating their own POE::Session  
instances, then you'd need to call() AnyEvent::Impl::POE to make sure  
the watchers are created in the right context.

sub new {
   # (AnyEvent::Impl::POE setup goes here)
   # set up the POE::Kernel watcher
   $poe_kernel->call("anyevent_impl_poe", "something", something);
   # make a note of the watcher in this object
   $self->{something} = $record_of_the_poe_watcher;
   return $self;
}

And DESTROY would tell the session when to clear the watcher.

You may need to add a new AnyEvent::Impl method to explicitly stop  
POE, especially if your public API allows users to exit with active  
watchers.

sub shutdown {
   $poe_kernel->signal($poe_kernel, "UIDESTROY");
   POE::Kernel->run();
}

As an added bonus, shutting down this way satisfies the run() warning.

I know this isn't a full solution, but I hope you still find it helpful.

> You kepe repeating how it could be designed better, but you never  
> actually
> say how to solve the fundamental problems and bugs within POE that  
> keep it
> from being implementable.

I would be welcome to discuss the code more than each-other.  If we  
can agree on this, perhaps we can get down to more important matters.   
See above.

> As I said, if possible, I can only imagine the design becoming  
> vastly more
> complex because I would have to create sdessions on demand and be  
> able to
> react to my session beign turned down at unopportune times.

I don't understand why this design is necessary.  Please help me  
understand your design constraints, so that I may focus on designs  
that will work.

> What we seem to agree on by now is that such a design is not trivial  
> to do
> with POE.
>
> Also, remember that the benchmarks show that session creation is not
> the big problem, running the sesions is - of course, there could be
> inefficiencies in POE handling large number of sessions, but that  
> means
> just that - POE doesn't scale well.

While my suggestions are not as trivial as your current design, I  
don't think the end design will be as complex as you expect.

Thank you for your feedback.  I'm sorry that POE doesn't meet your  
needs.  When I have the chance, I'll profile POE while running your  
benchmark and see what I can do.

> As the documentation mentions, AnyEvent doesn't enforce itself on a
> module, unlike POE - a module using POE is not going to work with  
> other
> event loops, because it monopolises the process.
>

> This means that a module using POE forces all its users to also use  
> POE.

This is factually incorrect.  For example, POE::Loop::Glib allows POE  
to be embedded into applications like vim and irssi.  The  
application's functionality is not impaired, nor is communication  
between the native scripting elements and the embedded POE code.

POE defers to the native event loop whenever possible.  For example,  
the main loop from POE::Loop::Gtk:

sub loop_run {
   unless (defined $_watcher_timer) {
     $_watcher_timer = Gtk->idle_add(\&_loop_resume_timer);
   }
   Gtk->main;
}

In cases where issues prevent POE from using the native loop, a custom  
loop is written around the native DoOneEvent() (or equivalent)  
dispatcher.

> This is a fundamental difference between POE and AnyEvent, it has  
> nothing
> to do with event loop backend modules, of which POE also emplys a  
> few, but
> comes form the fact that you have to call POE::Kernel->run and give up
> your process to it (just like with EV::loop etc.)

I believe the above loop_run() above shows this to be factually  
incorrect.  Even when POE::Kernel->run() is called, execution is  
passed back to the native event loop.

AnyEvent::Impl::POE doesn't relinquish control to POE::Kernel->run(),  
so it seems to conflict with your claim.

> On Mon, Apr 28, 2008 at 04:36:42AM -0400, Rocco Caputo <rcaputo@pobox.com 
> > wrote:
>
>> Are you aware that I'm gradually rewriting POE's documentation?  If
>> you could describe what you don't like in a useful way, I may be able
>> to do something about it.
>
> Since I described it already (and you know that) it means you find  
> the way
> I did it "not useful". Thats a strawman argument. If you don't like my
> criticism or don't understand it, ask.

Okay, I will ask.  In all cases, please include sufficient information  
in your reply so that I can find the documentation in question.

What is inaccurate?  How is it inaccurate?
What parts could I improve?  How could I improve them?
What is missing?
What isn't necessary?
If I've overlooked a question to ask, please let me know, and I will  
ask it.

> It is fine with me if you don't understand AnyEvent, it is somewhat  
> fine
> with me if you make strong (But wrong) statements about it, but don't
> expect anybody to put much faith in them, or you ability to make  
> useful
> statements.

Back at you.  See above for some examples where your strong statements  
against POE have been incorrect.

As I've said before, I'd like to resolve these misunderstandings so  
that we can move forward in a more amicable and efficient fashion.  If  
we can eventually exhaust our misconceptions by thoroughly addressing  
them, then perhaps we can move on to more productive things.

>> Assuming that N is the same between the equivalent POE and
>> AnyEvent::Impl::POE program:
>>
>> S(N*M) > S(N) for M > 1.
>>
>> QED  :P
>
> I couldn't really follow you here, and I am not sure what you have  
> proven. To
> me it certainly looks as if it was "POE cannot support the AnyEvent  
> API
> efficiently" (at leats not in a simple and straightforward way).

I don't know how to be more explicit, unambiguous, and emotionally  
neutral than by modeling the overhead mathematically.  If that's  
insufficient, then I'm at a loss for how to proceed.

>>> I can only imagine making some very complex on-demand  
>>> instantiating and
>>> re- check wether the session still exists on each watcher creation.
>>
>> Your imagination comes up with such incredible things.  Don't lose
>> that. :)
>
> Overbearing words after being wrong before.

My bad.  It's a long message, and I had to let off some steam.  On a  
more positive note, you do have an inventive mind, and I really do  
hope that you never lose that quality.

>> I use this design in POE::Stage.
>
> Does it survice multiple run's? If yes, how so?

I'm not sure that multiple run()s is a relevant issue.  Your code  
seems to get along without calling run() at all.

The number of POE::Session instances has no bearing on whether you can  
continue using loop_do_timeslice().

I am obviously overlooking something.  If you could explain what that  
is, we could put this detail to rest and move on.

>>> (the race condition with sigchld is really annoying,
>>
>> Please describe the race condition in a useful way, so that I can
>> investigate and possibly fix it.
>
> Again the red herring "useful" - it obviously was useful enough for  
> other
> people to understand the issue. The problem is that rgeistering a  
> watcher
> after the child process already exited causes POE to introduce a  
> delay in
> reapign the child (it does seem to do polling to do so, and seems to  
> do so
> once per second, so the delay is at most a second, still, thats a  
> pretty
> long time).

[more specifics snipped]

A useful report is one that can be use to replicate or isolate the  
problem.  Saying "the race condition with sigchld" is not useful.   
Being more specific, as you have just done, is VERY useful.  Thank you!

An experimental fix for what you describe has already been released.   
Because it's experimental, you will need to enable it manually.   
Please verify you're using the most recent POE, and check  
POE::Kernel's documentation for "USE_SIGCHLD".

It appears that the sig_child() documentation conflicts with the  
USE_SIGCHLD documentation.  I have fixed it for the next release.

>>> The "run() method was never called" message can be silenced by  
>>> calling
>> run().  Not all applications require run().  But if you call it
>> without any POE::Session instances, it will return immediately and
>> satisfy POE::Kernel.
>
> This is impossible it guarentee from within AnyEvent.
>
> So there is no way to silence just the message.
[...]
> AnyEvent::Impl::POE already invokes the run workaround in its  
> manpage, and
> why it is undesirable. This puts an unecessary burden on the user,  
> and is
> certainly an issue.

Your code already silences this warning, so I don't know why you keep  
saying it can't be done:

# have to do this to keep POE from spilling ugly messages
POE::Session->create (inline_states => { _start => sub { @_[KERNEL]- 
 >stop } });
POE::Kernel->run;

Unless AnyEvent is doing something I don't know, you should be able to  
drop the POE::Session->create() line without any harm.

>> You complain about a message that occurs when a program leaks a child
>> process.  Programs that leak processes are broken in a way that is
>> difficult to detect without the warning.  In extreme cases, process
>> leakage can quietly strangle a machine to death.
>
> No, I complain about the emssage complaining about POE reaping a  
> child. No
> leak is involved anywhere, and nothign will "quietly" "strangle"  
> anything
> to death.

The message I'm thinking of occurs at the end of the program when POE  
checks whether any child processes remain.  Exiting without reaping  
children is considered bad form and can lead to orphan processes on  
some systems, so POE enforces this behavior.  The warning indicates  
that the application has not been looking after its children.

POE doesn't reap children unless there is a sig_child() watcher.   
Therefore, if your program is long-running, spawns processes during  
its normal operation, and fails to trigger reaping, it will eventually  
exhaust its process resources.

If the application runs as root, there will often be no ulimit guard  
preventing zombie processes from filling up the system's process  
table.  If this happens, the system will most likely become  
unresponsive and need to be restarted.

I consider such applications to be broken.  You are welcome to think  
otherwise, but POE will continue to point out when this happens.

The most direct solution is for an application to set sig_child()  
handlers for the children it creates.  This will trigger reaping, and  
the warnings (and potential for catastrophe) will go away.

>> OMG, we agree on something!  The end times are here:

>
> sorry?

It's a joke.  You see, we agree on so little, so actually agreeing on  
something is a momentous event.  I was humorously drawing parallels to  
events that signal the impending end of the world in some religions.   
The "end times" are also conveniently where I finished my e-mail.   
It's a bit of a pun, really.

-- 
Rocco Caputo - rcaputo@pobox.com
Navigate in group perl.loop at sever nntp.perl.org
Previous Next




  
© No Copyright
You are free to use Anything
Site Maintained by PHP Developer
Powered By PHP Consultants