Re: Event design sketch

2004-05-14 Thread Rocco Caputo
On Tue, May 11, 2004 at 02:06:51PM -0400, Dan Sugalski wrote:
 At 1:10 PM -0400 5/11/04, Uri Guttman wrote:
 
 i don't think there is a need for all those variants. why would alarm
 need any special opcode when it is just a timer with a delay of abs_time
 - NOW? let the coder handle that and lose the extra op codes.
 
 I didn't see any reason to not do absolute times as well as deltas. 
 It's no big deal either way.

You may need the distinction later.

POE's mailing list has a thread with a periodicity of about six
months: What happens when daylight-saving time comes or goes?  The
answer: All your short-term timers fire at once if the clock moves
ahead, or everything stalls for an hour (or so) when it moves back.

So far the best solution requires a distinction between absolute and
relative timers:  Adjust the relative timers to compensate for the
shift, but leave the absolute ones alone.

Relative timers tend to be used for things like timeouts and periodic
polling.  You don't want all your TCP connections to suddenly time out
when the clock jumps ahead, and you don't want your log tailers to
pause an hour when it skips back.  However, you usually do want your
absolute-timed jobs to run when the wall clock says they should be.

Something to keep in mind.

-- Rocco Caputo - http://poe.perl.org/


Re: use parrot;

2001-10-21 Thread Rocco Caputo

On Sun, Oct 21, 2001 at 12:20:29PM -0400, Dan Sugalski wrote:
 
 I suppose. I hadn't planned on inlining parrot assembly into any other 
 language. (The first person who suggests an asm() function *will* get 
 smacked... :)  You'll certainly be able to use modules written purely in 
 parrot assembly.

1. B::Parrot
2. Parrot.xs
3. Providing opcodes for libperl functions and linking it in.

I haven't suggested asm(), so technically I'm safe.  Right?  :)

-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net



Re: Parrot multithreading?

2001-09-20 Thread Rocco Caputo

On Thu, Sep 20, 2001 at 12:33:54PM -0700, Hong Zhang wrote:
 
DS I'm also seriously considering throwing *all* PerlIO code into
  separate 
DS threads (one per file) as an aid to asynchrony.
  
  but that will be hard to support on systems without threads. i still
  have that internals async i/o idea floating in my numb skull. it is an
  api that would look async on all platforms and will use the kernel async
  file i/o if possible. it could be made thread specific easily as my idea
  was that the event system was also thread specific.

 I think we should have some thread abstraction layer instead of throwing
 PerlIO into threads. The thread
 abstraction layer can use either native thread package (blocking io), or
 implement user level thread package
 with either non-blocking io or async io. The internal io should be sync
 instead of async. async is normally
 slower than sync (most of unix don't have real async io), and thread is
 cheap.

I agree.  Threads, at least in spirit, provide a cleaner interface to
threading and asynchronous I/O than user level callbacks.  There's
nothing stopping a compiler from generating event driven code out of
procedural, perhaps even threaded code.  Consider this:
 
  sub read_console {
print while ();
  }
 
  sub read_log {
print while (LOG);
  }
 
  Thread-new( \read_console );
  Thread-new( \read_log );
  sleep 1 while threads_active();
  exit 0;

A compiler can either generate threaded code that's pretty close to
the original source, or it can generate asynchronous callbacks at the
bytecode level.  The same source code could compile and run on systems
that support asynchronous I/O, threads, or both.

Here's some parrot assembly that may or may not be legal at the
moment.  It shows what a compiler might do with the threaded source
code on a system that only supported asynchronous I/O.

read_console_entry:
  find_global  P1, STDIN, main
  set  I1, read_console_got_line
  jsr  set_input_callback
  return
read_console_got_line:
  # Assumes the AIO engine sets S1 with a read line.
  printS1
  return

read_log_entry:
  find_global  P1, LOG, main
  set  I1, read_log_got_line
  jsr  set_input_callback
  return
read_log_got_line:
  # Assumes the AIO engine sets S1 with a read line.
  printS1
  return

main:
  set  I1, read_console_entry
  jsr  thread_new
  set  I1, read_log_entry
  jsr  thread_new
main_timer_loop:
  set  I1, main_timer_done
  set  I2, 1
  jsr  set_timer
  return
main_timer_done:
  jsr  threads_active
  ne   I1, 0, main_timer_loop
  end

__END__

-- Rocco Caputo / [EMAIL PROTECTED] / poe.eekeek.org / poe.sourceforge.net



Re: Parrot multithreading?

2001-09-20 Thread Rocco Caputo

On Thu, Sep 20, 2001 at 04:13:48PM -0400, Michael L Maraist wrote:
 
  What we're going to do is fire up a new interpreter for each thread. (We
  may have a pool of prebuilt interpreters hanging around for this
  eventuality) Threading *is* essential at the parrot level, and there are
  even a few (as yet undocumented) opcodes to deal with them, and some stuff
  that's an integral part of the variable vtable code to deal with it.
  Whether it's considered ithread-like or not's up in the air--it'll probably
  look a lot like a mix.
 
  I'm also seriously considering throwing *all* PerlIO code into separate
  threads (one per file) as an aid to asynchrony.
 
 Just remember the cost in context-switching, plus the lack of scalability as
 the number of file-handles increases.  Linux thread-context-switches are
 relatively brutal compared to say Solaris.  Additionally you're consuming a
 new stack area for each file-handle.  That's lots of overhead.


One idea I haven't seen mentioned is have a fixed number of system
threads to service a potentially larger pool of parrot interpreters.
Essentially, physical threads become execution pipelines for the
virtual machine.  The limit on system threads can be tuned to
optimally spread execution across available CPUs.  It could be as
small as 1 on single-processor systems that don't switch thread
contexts well.

-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net





Re: Calls for a Macro Assembler Guy

2001-09-14 Thread Rocco Caputo

On Fri, Sep 14, 2001 at 11:20:36AM -0400, Dan Sugalski wrote:
 Okay, we've had a number of people in favor of a good macro assembler for 
 Parrot. Given that, do we have anyone who'll volunteer to define, maintain, 
 and extend the thing? (Presuming we jump off of the current assembler, 
 which seems reasonable)
 
 There probably isn't a huge amount to do with the thing--maintain macro 
 substitutions, handle local labels, manage sub definitions, and suchlike 
 things.
 
 Anyone?

j0.  I'll do it.

-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net



conveniences

2001-09-14 Thread Rocco Caputo

The attached assemble.diff has assemble.pl look in its parent
directory if the data files it needs aren't in the current one.  This
lets me camp out in t/ and assemble stuff over and over.

The attached Makefile goes in the t/ directory.  It lets me reassemble
files by invoking make test.pbc.

-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net


--- assemble.pl-origFri Sep 14 11:20:05 2001
+++ assemble.pl Fri Sep 14 12:26:38 2001
@@ -59,7 +59,9 @@
 
 
 # get opcodes from guts.
-open GUTS, interp_guts.h;
+open(GUTS, interp_guts.h) or
+  open(GUTS, ../interp_guts.h) or
+  die Can't get opcodes from guts, $!/$^E;
 my %opcodes;
 while (GUTS) {
 next unless /\tx\[(\d+)\] = ([a-z0-9_]+);/;
@@ -68,7 +70,9 @@
 close GUTS;
 
 # get opcodes and their arg lists
-open OPCODES, opcode_table or die Can't get opcode table, $!/$^E;
+open(OPCODES, opcode_table) or
+  open(OPCODES, ../opcode_table) or
+  die Can't get opcode table, $!/$^E;
 my $opcode_table;
 while (OPCODES) {
 $opcode_table .= $_;


# $Id$
# Convenient makefile for assembling files.

.SUFFIXES: .pbc .pasm

.pasm.pbc:
perl ../assemble.pl $  $@



macros

2001-09-14 Thread Rocco Caputo

Attached is a patch to assemble.pl that adds very simple macros.  I
fear it's a bit of a hack, but I'm fighting my usual impulse to
rewrite stuff.

Attached is also macros.pasm, a simple usage case.  It goes in t/ for
want of a better place, but it's not a true test yet.

-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net


--- assemble.new-1  Fri Sep 14 12:26:38 2001
+++ assemble.pl Fri Sep 14 13:31:46 2001
@@ -93,24 +93,77 @@
 
 my $listing=PARROT ASSEMBLY LISTING - .scalar(localtime).\n\n;
 
-
+# Macro stack.
+sub MACRO_FILE () { 0 }
+sub MACRO_LINE () { 1 }
+sub MACRO_NAME () { 2 }
+sub MACRO_ARGS () { 3 }
+sub MACRO_BODY () { 4 }
+my (@macro_stack, %macros, @input_buffer);
 
 # read source and assemble
 my $pc=0; my $op_pc=0;
 my ($bytecode,%label,%fixup,%constants,@constants);
 my $line=0;
-while() {
+while(1) {
+my $sline;
+if (@input_buffer) {
+$_ = shift @input_buffer;
+}
+else {
+$_ = ;
+last unless defined $_;
 $line++;
 chomp;
-my $sline=$_;
+$sline=$_;
 s/^\s*//;
 s/\s*$//;
+}
+
+# Comments and blank lines.
 if(/^\#/ || $_ eq ) {
if($options{'listing'}) {
$listing.=sprintf(%4d %08x %-44s %s\n, $line, $op_pc, '',$sline);
}
next;
 }
+
+# Macro definition.
+if (m/^MACRO\s+(\S+)\s*(.*?)\s*$/) {
+  my ($label, $args) = ($1, $2);
+  my @args = split /, */, $args;
+
+  die macro $label redefined at STDIN line $.\n
+if exists $macros{$label};
+
+  $macros{$label} =
+[ 'STDIN',  # MACRO_FILE
+  $.,   # MACRO_LINE
+  $label,   # MACRO_NAME
+  \@args,   # MACRO_ARGS
+  [ ],  # MACRO_BODY
+];
+
+  push @macro_stack, $label;
+  next;
+}
+
+# Macro done.
+if (m/^MEND/) {
+  my $label = pop @macro_stack;
+
+  # XXX: Verify that the macro uses all of its arguments at least once.
+
+  next;
+}
+
+# Within a macro.
+if (@macro_stack) {
+  push @{$macros{$macro_stack[-1]}-[MACRO_BODY]}, $_;
+  next;
+}
+
+# Opcodes with labels, or just labels by themselves.
 if(m/^((\S+):)?\s*(.+)?/) {
 my($label,$code)=($2,$3);
 if(defined($label)  $label ne ) {
@@ -173,7 +226,24 @@
last;
}
}
-error(No opcode $opcode in $_) if(!$found_op);
+
+# Not an opcode.  Is it a macro?
+unless ($found_op) {
+  error(No opcode or macro $opcode in $_) unless exists 
+$macros{$opcode};
+  error(Macro $opcode parameter count doesn't match definition) unless 
+@args == @{$macros{$opcode}-[MACRO_ARGS]};
+
+  # Expand the macro and push it on the input stream.
+  my @macro_body = @{$macros{$opcode}-[MACRO_BODY]};
+  my @macro_args = @{$macros{$opcode}-[MACRO_ARGS]};
+  foreach my $macro_line (@macro_body) {
+for (my $arg_index = 0; $arg_index  @args; $arg_index++) {
+  $macro_line =~ s/\b$macro_args[$arg_index]\b/$args[$arg_index]/g;
+}
+  }
+
+  push @input_buffer, @macro_body;
+  next;
+}
 }
 if (@args != $opcodes{$opcode}{ARGS}) {
 error(Wrong arg count--got .scalar(@args). needed 
.$opcodes{$opcode}{ARGS});
@@ -219,6 +289,14 @@
 }
 }
 $listing.=\n if($options{'listing'});
+
+# Macro stuff.  Make sure all the macros were closed.
+my $bad_macros = 0;
+foreach (@macro_stack) {
+  $bad_macros++;
+  warn macro $_ not closed (defined in $macros{$_}-[MACRO_FILE] at 
+$macros{$_}-[MACRO_LINE])\n;
+}
+die assembly stopped.\n if $bad_macros;
 
 my $output;
 


# Todo : Local labels (begin with dollar sign)
# Todo?: NAMESPACE (namespace name)
# Todo?: SUB (sub name)
# Todo : symbol  IS  string   ; string
# Todo : symbol  IS  1234   ; integer
# Todo : symbol  IS  3.141  ; float
# Todo : symbol  IS  ???; memory (variable)

MACRO   inc_and_print   register
inc register
print   register
print   \n
MEND


start:  set I1, 0
set I2, 10
loop:   inc_and_print   I1
eq  I1, I2, done, loop
done:   end



Re: Between-Opcode Callbacks

2001-07-07 Thread Rocco Caputo

On Sat, Jul 07, 2001 at 05:51:00PM -0500, Jarkko Hietaniemi wrote:
  Not that innovative, really. :) Will basic blocks ever be different
  from scopes?
 
 The Book of the Red Dragon sayeth, p 528 in my copy:
 
   A basic block is a sequence of consecutive statements
   in which flow of control enters at the beginning and
   leaves at the end without halt or possibility of
   branching except at the end.

[...]

I suggested something similar in 1997:
http://www.xray.mpe.mpg.de/mailing-lists/perl5-porters/1997-02/msg00229.html

The message proposed a tasking package that would dispatch atomic
chunks of code.  I eventually wrote one in Perl; it's on the CPAN as
POE.  In the Perl version, code atoms are just carefully coded subs.
They would be basic blocks in a bytecode dispatcher.

I still like the idea, so a few months ago I wrote a prototype of it
in C for perl6-internals to tinker with.  It's called phat, there is
some discussion about it in the list archive, and the working
prototype (for prototypical values of working) is still at
http://poe.perl.org/phat/phat.c.

-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net



ideas for signalling threads (was: Re: Does perl really need to use sigsetjmp?)

2001-01-21 Thread Rocco Caputo

On Sun, 21 Jan 2001 17:28:49 -0500, Dan Sugalski wrote:

The issue of 'which thread' comes in with signals. We can tell with real 
async events, since they'll have thread ownership tags on them if we care, 
but we can't do that with signals, and then we have the issue of "which 
thread" to deal with?

Here's how I've dealt with signals in a cooperatively threaded Perl
library.  This design probably is too simple for Perl6, but it might
be useful to consider.


The library maintains threads' parent/child relationships.  Each
thread is something of a session leader for the threads beneath it, at
least where signals are concerned.  Signal events arriving at a thread
are first propagated to its children, and this repeats recursively
down to the leaf nodes on the family tree.  In essence, signal
delivery happens from the leaf threads back up to the ancestors they
were originally destined for.

Operating system signals are posted to the library's "kernel", which
is the ultimate ancestor of every thread.  This effectively (although
perhaps not efficiently) broadcasts OS signals to every thread.

The library's kernel decides which signals are fatal.  It can do this
because it's also the event dispatcher.  Threads that don't handle
fatal signals are stopped.  Each thread has a destructor, so
everything gets a chance to clean up, even after fatal signals.

The library has an extra signal class: nonmaskable.  Every thread that
receives a nonmaskable signal is stopped regardless whether it handled
the signal.  Nonmaskable signals tend to be fictitious.  They are
generated by the library itself, and they're usually meant to signal a
global shutdown.  Two examples:

UIDESTROY is broadcast when a GUI's main window is closed.  The user
wants this program to stop, and so it shall.

ZOMBIE is broadcast when every thread has stopped running, and nothing
is available to restart them.  Imagine two threads playing ping-pong
with an event.  If one drops the ball, both threads' callbacks keep
them alive.  A program can detect when it has no more balls in play
(the queued-event counter is 0) and shut itself down.  (It's a little
more involved than this: Watched filehandles are significant, for
example, so there's a counter for those, too.)


Here are two things Perl6 can do that my library doesn't, but I'm
going to recommend against them in a moment.

Perl6 will know which thread forked which process; it can track
this information to send SIGCH?LD to appropriate threads.

The system's SIGPIPE can be ignored altogether.  Wherever a PerlIO
function returns EPIPE, it can also post SIGPIPE to the same thread.

I haven't implemented thess is my library because they preclude the
ability to set global SIGCHLD or SIGPIPE handlers in some totally
unrelated thread.  Someone out there will want to do this, and it
might just be me.  It helps that these tend to be "survivable"
signals; only the interested threads will ever notice them even when
they're broadcast everywhere.


Apropos of Perl 5: I've experimented long and hard on the language
level in perl 5, and I have a test case for detect them without using
handlers.  It's unreliable though.  That is, it may miss duplicate
signals between checks.  When is this an issue, though?

-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net





callbacks, aio, threads and events (and a working C prototype)

2001-01-08 Thread Rocco Caputo

Good morning.

I've been thinking about defining atomic code units for a while, and I
even posted something about it to p5p a few years back.  The idea has
matured since then, and I'd like to quickly outline it here:

I've been defining code atoms using three rules:

1. Atoms begin at branch destinations.
2. Atoms end after each AIO-able opcode has set up a callback.
3. Atoms end after branches are taken.

Before I continue on to explain how this ties into callbacks, AIO,
threads, and events, I'd like to direct your attention to a C program
I wrote.  This program implements a tiny AIO subsystem (just timers),
an opcode atom dispatcher, and a main loop.  It suports at the opcode
level the features needed for callbacks, AIO, threads and events at
the language level.

I submit for general consideration http://poe.perl.org/phat/phat.c.
It may be compiled the usual way: gcc -Wall -o phat phat.c

The rest of this message outlines what phat's design does or can be
extended to do.

Back to opcode atoms.  Consider, for example, this little loop.  phat
runs ten of them, two each in five opcode interpreters.

  while (1) {
sleep(5);
print "something about the sleep call";
  }

After compiling and optimizing, the opcodes might look like this.

  label_1:
sleep 5
  label_2:
print
goto label_1

That's two atoms.  The first ends after the sleep opcode has run,
because it can be implemented in terms of AIO.  The second ends after
the goto has changed the interpreter's opcode pointer.  While these
atoms look like they end at statement boundaries, they can extend
farther than that.

The sleep opcode isn't allowed to block.  Instead it asks the AIO
subsystem to wait asynchronously, and it ends the atom.  If the opcode
interpreter has no pending events in its queue, it marks itself
"blocked" before returning to the main dispatch loop.

The main dispatch loop will ignore the interpreter as long as it's
blocked.

Part of the AIO timer request includes the address for label_2.  This
effects an ad-hoc callback at the opcode level.  It asks AIO: "resume
this interpreter at label_2 in five seconds".

In five seconds, the AIO subsystem posts a wake-up event to the
interpreter's queue, including the address of label_2.  The act of
enqueuing an event will reawaken an interpreter if it's currently
marked as blocked.  The main dispatch loop will then service the
interpreter, and the atom will be executed.

Callbacks?  AIO?  Threads?  

Each interpreter's queue holds events which tell it which atoms to run
next.  It's something of an atom execution pipeline.  AIO and callbacks
are just like what OP_SELECT does in phat, only they're exposed at the
Perl level.

Threads are implemented in as interleaved "run this atom" events in an
interpreter's queue.  Each event includes the address of the atom it
will invoke.  This simulates multiple instruction pointers, each taking
its turn as the interpreter dispatches events to their appropriate
atoms.

Callbacks and threads coexist because they use the same underlying
mechanism: the interpreter's execution queue.

What about system threads?

"Threads" within an interpreter aren't pre-emptive, but they do
present a consistent notion of threading at the Perl level.  That is,
the Thread module would work even where system threads aren't present.

System threads are still useful, though, and each interpreter can run
in a separate one, where they're available.  This allows two or more
interpreters to run concurrently, whereas the "threads" in any given
interpreter are still interleaved chunks of atomic code.

With each interpreter running in its own thread, the main loop can
focus on AIO.  AIO events would still be posted to each interpreter's
queue, but but the interpreters' sleep/wake cycles are handled by
semaphores instead of timeslice calls from the main loop.

In this way, threads prevent external libraries (for example, a DBD)
from blocking entire programs.  They would only block the interpreters
they're running it; the other interpreters (and the main loop, and
AIO) would be free to continue on.

And language level events?

Finally, if the per-interpreter event queues are exposed at the Perl
level, they can be used for asynchronous inter-thread communication.
Threads in one interpreter could post requests to a DBI module running
in another.  The client threads would be permitted to continue on,
eventually receiving DBI responses as asynchronous callbacks.

I think that's it.  Thank you for reading.

-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net





Re: Speaking of signals...

2001-01-06 Thread Rocco Caputo

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





Re: Speaking of signals...

2001-01-06 Thread Rocco Caputo

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





Re: Events and threads

2001-01-06 Thread Rocco Caputo

On Sat, 06 Jan 2001 13:09:47 -0500, Dan Sugalski wrote:

Okay, here's a big question that ties the two major pains we have in perl 
6--how do we tie threads and events together?

* Can all events be delivered to any thread?

Yes, but in practice events probably would only be delivered to
threads that were listening for them.  Signal events are different
beasts, though, although you could consider Clocal $SIG{INT} =
\handler; as listening for an event.


* Will events be delivered only to the thread that caused them? If so, what 
about threadless events? (Say, signals from the outside world)

Not necessarily.  Events can be a useful way to pass messages between
threads.  It might be fun to say:

  post( $thread, event_name = @args );

Signals are different.  I've written in Perl an event driven thread
dispatcher which has two different signal delivery modes.  To its best
of its ability, it dispatches SIGPIPE to the thread where it was
generated.  Everything else is propagated to every thread.

Some signals, like SIGINT, are terminal.  Threads that don't handle
them are shut down.  You can get situations where some threads shut
down on SIGINT and others continue running, perhaps to save things.


* Should events be filtered into multiple event queues? If so, how do we 
say which goes where?

I've worked out some of this for a 5005threads-aware version of my
dispatcher.  The prototype I wrote uses one queue per thread.  Events
destined for a particular callback go to the thread where that
callback lives.


* Should async events see any state at all, or should they live in a Safe 
compartment (or something like it)?

This wasn't an issue in my dispatcher.  I had intended to go the route
of a higher-level language written in terms of hidden asynchronous
I/O (event driven threads).  All language-level I/O would appear
synchronous, except for signals, which are, again different beasts.


Opinions? Now's the time when we must address this, so better weigh in with 
opinions before I choose... :)

The thread-aware prototype is at http://poe.perl.org/poe2/.
current-snapshot.tar.gz in that directory is a tarball of the whole
tree.  Design notes are in Readme, the notes directory, and source
comments.


-- Rocco Caputo / [EMAIL PROTECTED] / poe.perl.org / poe.sourceforge.net