All right, the key word "flow" has been uttered. Here's the flow:
1. lambda {X} has just been called. It returns an empty object, blessed hash of IO::Lambda with a single coderef X,
which is not executed yet. This object has a method wait(),
which is then called.
2. wait() figures out that lambda (object, that is) hasn't been started yet. It switches the lambda into "started" state, and invokes callback X. Then it enters event loop sleep/select cycle, until the lambda state gets "finished":
sub wait # simplified
{
my $lambda = shift;
$lambda-> start if $lambda-> is_passive;
event_loop while not $lambda-> is_finished;
return $lambda-> peek;
}
3. Coderef X creates a socket, calls a non-blocking TCP connect on it using IO::Socket::new, and passes that socket,
along with a coderef Y to standard condition writable:
context $socket;
writable { Y }
and then exits. The lambda state is unchanged by calling writable, however it adds the socket into the listening queue of the underlying event loop (select, AnyEvent, POE, whatever), and asks the loop to do something
when that socket gets writable. Meanwhile, wait still
processes the event loop.
4. TCP syn-synack-ack succeeded, event loop says the socket is writable, and calls the lambda that waits for the socket. Lambda figures out that the socket waiting conditions should result in calling the coderef Y, which it immediately does.
5. Coderef Y does two things: it prints to the socket, and registers another coderef Z, that will be called when the socket gets readable:
syswrite $socket ....;
readable { Z }
Note, that these callbacks are one-shot only. The subscription to the writable events is not renewed, so the socket is not listened for write events anymore.
6. Socket gets readable, Z gets executed. It reads some bytes, but wants more, so it re-subscribes on the same socket, with the same coderef Z, to be woken up when there are more data. This is done with calling again. The latter knows the last coderef used, and spares the programmer from calling readable with the same coderef.
7. Socket gets readable, Z gets executed again. It reads zero bytes, figures it's an EOF, it says "return $buf". Before that, result of the last executed coderef was accumulated inside the lambda object. Now, $buf overwrites that and is stores its own $buf, which is
then accessible through method peek.
8. Lambda, having called coderef Z, finds out that it has no more subscriptions, so it switches itself to "finished" state.
9. wait sees that the lambda is finished, it stops the cycle and and returns whatever was left on lambda, in our case $buf.
I almost feel how it gets better :) Please tell me what do you think. |