On Fri, 5 Jan 2001 16:47:25 -0500, Uri Guttman wrote:
>>>>>> "DS" == Dan Sugalski <[EMAIL PROTECTED]> writes:
>
> DS> I'm less worried about long running ops (whose fix is just a SMOP,
> DS> after all... :) than I am blocked ops. We can be as clever as we
> DS> want with event dispatch and async handling but it won't do us a
> DS> darned bit of good if the interpreter's stuck waiting on a read
> DS> from a filehandle or something.
>
> DS> We can, I suppose, declare "No blocking system calls!" and let
> DS> folks stuck with older OSes deal with it as best they can, but I'm
> DS> not sure I like that a whole lot.
>
>yuck. if the perl level code makes a blocking call, that is their
>problem. if you want proper events and signals and async i/o you have to
>be prepared to handle callback delivery. by doing blocking calls you
>ruin that yourself. we can never fix bad programming at the perl
>level. i think inline callback delivery is a crutch but i think too many
>people seem to want it as they can't deal with event loops. and even i
>would want it for some simpler cases where i didn't want to make up
>events like with handling basic signal stuff.
With a tightly integrated event loop, blocking perl level I/O can be
implemented in terms of internal asynchronous I/O. An interpreter can
then block while perl is free to do other things, like say run other
interpreters.
Consider a simple readline loop:
while (<FILE>) {
print;
}
Here's some hypothetical pseudocode. The opcodes assume a stack
machine, but I'm sure something similar can be done with registers.
label_1:
read_line FILE
branch_if_stack_0_is_undefined label_2
print_line
goto label_1
The read_line opcode might be implemented as:
If a line is available, push it on the stack and return.
Arrange for the I/O subsystem to read a line asynchronously. When
the subsystem receives a line, it will resume this interpreter
context, putting the line on its stack.
Pause the interpreter context, and return to the dispatch loop.
The print_line opcode might be:
Take a line from the stack, and give it to the I/O subsystem for
writing.
If the I/O system could flush it immediately (depending on autoflush
and buffering), then return the number of characters written.
Otherwise, the I/O system will resume this interpreter context,
putting the character count on its stack, when the line finally
could be flushed.
Pause the interpreter context, and return to the dispatch loop.
Pausing and resuming an interpreter is left as an exercise for the
interpeter's subsystem. The pair, though, turn into a sort of
bytecode level callback: call me back, branch to this label, or just
resume after the "blocking" perl level call when the asynchronous
task is done.
"blocking" timers can also be implemented this way:
sleep(5);
...;
becomes:
label_1:
branch to label_2 in 5 seconds
pause this interpreter
label_2:
...;
And finally, if interpreter contexts are subsumed into the notion of
threads, then threads can be implemented in terms of them. Or they
can be implemented in terms of system threads, where they're available
and desirable.
Consider:
use IO::Socket;
my $server = IO::Socket::INET->new(
LocalPort => 2007, # echo + 2000
Type => SOCK_STREAM,
Reuse => 1,
Listen => 5,
);
while (1) {
my $client = gensym();
if ( accept( $client, SERVER ) ) { # async I/O inside
Thread->new( \&client_handler, $client ); # called in a new context
}
}
sub client_handler {
my $client = shift;
while (<$client>) { # async I/O inside
print $client $_; # async I/O inside
}
}
This could very well be an event driven program, with all the tedious
mucking about with callbacks done under the hood. Regardless, it could
run the same as long as either threads OR async I/O are available.
-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net