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


Reply via email to