On Fri, 5 Jan 2001 23:46:33 -0500, Uri Guttman wrote:
>>>>>> "RC" == Rocco Caputo <[EMAIL PROTECTED]> writes:
>
> RC> With a tightly integrated event loop, blocking perl level I/O can be
> RC> implemented in terms of internal asynchronous I/O. An interpreter can
> RC> then block while perl is free to do other things, like say run other
> RC> interpreters.
>
>doing this at the perl language level is easy since we control the stack
>and program counter. try doing it at the c level in the guts without
>munging the stack in ugly ways. by having the guts of perl always assume
>it is running in an event loop, we can get all these features without
>any ugly code.
We seem to be in agreement, which is either really good, or really
bad. :) Here's more detail into the implementation I have in mind.
There is a set of opcodes-- mainly I/O and sleep-- that can be
completed asynchronously if they block. The functions that implement
these opcodes would never really block; instead, they would put
interpreters to sleep and arrange for the AIO subsystem to wake them
up when the operations complete.
This simulates blocking in Perl programs without blocking perl's
opcode dispatcher or the AIO subsystem's event loop. It does this in
a way (always returning) that eliminates swapping C's stack (or Java's,
or whatever's) and recursion.
Furthermore, if Perl provides a way to spawn new interpreters (perhaps
hidden within Thread), this gives people the opportunity to write
event driven programs as if they were threaded. At first blush, the
tiny TCP server at the end of <[EMAIL PROTECTED]>
looks threaded, but it's really running under AIO.
Or perhaps it uses real threads on systems where they're available,
but it uses AIO where threads aren't viable. I have a prototype of
something similar written in Perl; it uses 5005threads if they're
available, but it will use AIO where they aren't. It also allows
threaded runtime contexts and AIO-driven contexts to run together.
Back to non-blocking opcodes. Consider reading a line, as if C<< $x =
<FILE>; >> were called. Here's a hypothetical op_readline algorithm:
1. Try to read a line from the PerlIO subsystem. PerlIO returns
immediately if it fails.
2. If a line could be read in step 1, push it on the interpreter's
stack and return to the opcode dispatch loop. We're done.
3. Otherwise arrange for the PerlIO subsystem to read the line
asynchronously. Have it call op_readline_complete when a line has
been read, giving it these parameters: a pointer to this
interpreter thread, and the results of the asynchronous readline.
4. Flag the interpreter thread as "blocked". This stops the Perl
program in what appears to be a blocking readline() call, but the
opcode/event dispatch loop can continue on.
5. Return to the opcode dispatch loop, which may decide to schedule
opcodes in some other interpreter thread, or it may just sit idle
if everything is blocked.
Here's op_readline_complete, at least in theory:
1. It is called with a pointer to the interpreter that requested the
asynchronous readline and the readline's result.
2. Push the readline result on the interpreter stack.
3. Flag the interpreter thread as "running" or "unblocked" once again.
This looks as if the Perl program's <FILE> call has unblocked.
4. Return to the opcode dispatch loop.
These are sample opcodes for a simple file grep function, with the
corresponding Perl code in comments.
1: count_matches: # sub count_matches
2: pop $string # my ($filename, $string) = @_;
3: pop $filename
4: zero $found # my $found = 0;
5: push FILE # open FILE, "<$filename";
6: push "<"
7: push $filename
8: strcat
9: op_open
The interpreter can continue on to opcode 10 if op_open succeeds right
away. Otherwise the interpreter is flagged as "blocked", and program
control returns to perl's opcode dispatch loop.
The interpreter resumes where it left off when op_open completes
asynchronously.
10: label_1:
11: push FILE # while (<FILE>) {
12: op_readline
op_readline was covered in detail above.
12: pop $_
13: j_undef label_3
14: push $_ # $found++ if /\Q$string/;
15: op_match /\Q$string/
16: j_undef label_1
17: inc $found
18: jump label_1 # }
19: label_3:
20: push FILE # close FILE;
21: op_close
op_close may also pause and later resume the interpreter.
22: push $found # return $found;
23: return # }
End.
-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net