[Israel.pm] The Coro module

Yuval Kogman nothingmuch at woobling.org
Sun Dec 20 10:42:54 PST 2009


2009/12/20 Shmuel Fomberg <semuelf at 012.net.il>:
> Hi Yuval.
>
> Well, is it stable? won't crash my Perl too often?

Generally speaking, no. It's a pretty sane implementation/design.

There are scenarios where the assumptions could break down, but i've
never actually seen them in real code (tricky stack bound resource
management in XS code).

> I'm trying to use it with AnyEvent/EV.
> Is there a killer point for using it at all?
> because I saw that I can't block inside the event loop. So if I do
> things event-based, what benefits does Core gives me?

It let's you have a blocking abstractions, so you don't need to invert the code.

It's true that you can't block inside the event loop, but you can
block in other threads.

The event loop runs in a single coro, and the event handlers run in other coros.

When an event handling thread is waiting on a resource, it blocks, and
at some later point the event loop thread will wake it up.

This way calling a method like read in Coro::Handle actually does this:

1. try to read the data from the buffer. if it's available, return immediately
2. install an AnyEvent watcher for the filedescriptor, that invokes
Coro::rouse_cb
3. call Coro::rouse_wait

these steps are repeated till enough data has been read.

When rouse_wait is called the current thread is suspended, and some
other thread is given control.

The event loop thread will get control when all event handler threads
are idle. It will register the the watcher, and if there is nothing
else for it to do, make a blocking select/epoll/whatever call.

When the system call returns data is available, and the event loop
will fire the appropriate handler, in this case the code ref returned
by rouse_cb, which will wake up the event handler thread that is
waiting on the data.

This means that the event handler code looks like blocking code:

    $handle->read(my $buffer, $length);

    ...; # use $buffer

instead of callback driven code:

    $handle->read($length, sub {
        my $buffer = shift;

        ...; # use $buffer
    });

the biggest advantage is that you don't have to keep track all of the
values you'll need in the event handler (directly or indirectly), you
just use lexical variables as usual.

With normal event driven code you often need to do complicated
resource management (see below...) and you need to break up your code
into a trillion callbacks.

> anyway? I read the documentation about C context and
> Perl interpreters, but haven't understood anything.

I don't think there's a simpler way to explain it than with the docs...

For pure perl threads a context switch involves swiping one set of
pointers for another, so that you have multiple stacks, only one of
which is actively being used at any given moment in time.

In the perl core C code there is a struct that holds all of the values
needed to execute Perl code, and Coro changes the pointers in this
struct when performing a context switch.

For threads that have called into C it's a little more complex, since
the C stack needs to be captured as well, but the principle is the
same... That's done using ucontext/longjmp/pthreads/etc

> And last, I'm having problem waiting for STDIN to be available. AnyEvent
> don't call my callback for the io watcher. I'm working on Perl 5.10,
> Windows. Any idea?

Got example code?

It's probably a resource allocation issue, if nothing keeps the
watcher handle in scope it will be garbage collected and then watcher
will be canceled in the event loop.

The typical idiom is:

my $w;
$w = AnyEvent->blahblah( ... sub {
    ...
    undef $w;
})

that will:

1. keep a circular reference to the watcher using the closure
2. break that cycle once the callback has fired

and of course for more complicated systems you usually use a managed
approach, by just keeping track of live watchers.


More information about the Perl mailing list