The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
$Id$

A brief, pointless history of POE's evolution.

-------------------------------------------------------------------------------

Received: from sinistar.idle.com (sinistar.idle.com [198.109.160.36])
        by anshar.shadow.net (8.7.3/8.7.3) with ESMTP id JAA05315
        for <troc@shadow.net>; Fri, 7 Feb 1997 09:59:05 -0500 (EST)
Received: (from slist@localhost) by sinistar.idle.com (8.7.5/8.7.3)
        id JAA12501; Fri, 7 Feb 1997 09:00:15 -0500 (EST)
Resent-Date: Fri, 7 Feb 1997 09:00:15 -0500 (EST)
Message-Id: <199702071400.JAA00295@anshar.shadow.net>
From: "Rocco Caputo" <troc@shadow.net>
To: "Felix Gallo" <fgallo@wellspring.us.dg.com>,
        "perl5-porters@perl.org" <perl5-porters@perl.org>
Date: Fri, 07 Feb 97 08:54:23 -0400
Reply-To: "Rocco Caputo" <troc@shadow.net>
Priority: Normal
Subject: portable multithreading
Resent-Message-ID: <"O2kshC.A.W5C.lTz-y"@sinistar>
Resent-From: perl5-porters@perl.org
X-Mailing-List: <perl5-porters@perl.org> archive/latest/135
X-Loop: perl5-porters@perl.org
Precedence: list
Resent-Sender: perl5-porters-request@perl.org
Content-Type: text
Content-Length: 3989
Status:   

On Thu, 06 Feb 1997 12:52:56 +0000, Felix Gallo wrote:

>Felix's Perl-related Metaproblems:
>
>1.  Perl is not event-driven, so programs which wish
>to make objects available to the network must manually
>interrupt their control flow to determine if a remote
>object transaction request is pending.

I'm writing a MUD in perl.  The object language faces
some of the same issues as perl, but I think there are
ways around them (in the MUD language and in perl).  In
the MUD server, objects' methods must be compiled into
perl bytecode.  They must be multitasked/multithreaded
so that bad code won't hang the server, and object
authors usually should not have to think about events.

For example, this "bad" MUD code will be legal.  Don't
worry, I move on to perl in just a minute.

  count = 10000000
  while count--
    say "hello, world!  enter some text: "
    getline some_text
    if some_text eq 'quit'
      last
    endif
  endwhile
  say "\ngoodbye, world!\n"

This needs to be compiled to perl bytecode at runtime.
The MUD bytecode compiler first parses and syntax
checks an object's source.  If everything passes, it
builds a perl sub definition in a string.  This
sub-in-a-string is treated as an assembly language for
perl bytecode.  The server runs eval() to assemble the
string-o-perl into bytecodes, and then the resulting sub
can be called over and over without additional eval()
overhead.  (Thanks, Silmaril!)

Making that bad loop work in an event-driven server is
a little harder than making bytecodes.  The MUD compiler
will build perl assembly as event-driven state machines.
It can do this by noting the locations of branch
destinations and returns from blocking calls.  Each of
these locations starts a new atomic "state", and an
"instruction pointer" determines which state to run next.

Here's the event-driven perl "assembly" for that sample
MUD code.  It's not very efficient, but it serves for
illustration.

  sub aaaaa {
    # assumes the existence of a tasking/event kernel
    my $task = shift;
    my $namespace = $task->{"namespace"};
    my $ip = $task->{'instruction pointer'}; # state

    # initial entry point
    if ($ip == 0) {
      $namespace->{'count'} = 10000000 ;
      $task->{'instruction pointer'} = 1;
    }
    # top of while loop
    elsif ($ip == 1) {
      if ( $namespace->{'count'} -- ) {
        $task->say( qq(hello, world!  enter some text: ) ) ;
        # soft block on 'getline'
        $task->{'blocking'} = 'getline';
        $task->{'instruction pointer'} = 2;
      }
      else {
        $task->{'instruction pointer'} = 3;
      }
    }
    # "return" from getline
    elsif ($ip == 2) {
      $namespace->{'some_text'} = $task->getline();
      if ( $namespace->{'some_text'} eq q(quit) ) {
        $task->{'instruction pointer'} = 3;
      }
      else {
        $task->{'instruction pointer'} = 1;
      }
    }
    # after endwhile
    elsif ($ip == 3) {
      $task->say( qq(\ngoodbye, world!\n) ) ;
      $task->{'instruction pointer'} = -1; # signals end
    }
  }

The main select/event loop would have some code to run tasks
round-robin.  Something like this, but probably including code
to deal with priorities.

  if ($next = shift(@task_queue)) {
    if (($next->{'blocking'}) || ($next->run() != -1)) {
      push(@task_queue, $next);
    }
    else {
      undef $next;
    }
  }

And starting a new task might look like this:

  $task = new Task($tasking_kernel, "count =  ...  world!\n");
  if ($task->has_errors()) {
    $task->display_errors();
    undef $task;
  }
  # otherwise the task has been compiled and registered
  # with the $tasking_kernel

Anyway, that's how I'm writing portable multitasking for a
syntactically simple MUD language.  To make this work for
perl, there would be a standard tasking package, and perl's
bytecode compiler would need to modify its output to work
with the package.  Sort of like how the perl debugger works.

Just some ideas to ponder.

Rocco
<troc@shadow.net>

-------------------------------------------------------------------------------

Received: from sinistar.idle.com ([198.109.160.36])
        by anshar.shadow.net (8.8.5/8.7.3) with ESMTP id VAA13861
        for <troc@shadow.net>; Mon, 14 Apr 1997 21:04:07 -0400 (EDT)
Received: (from slist@localhost) by sinistar.idle.com (8.7.5/8.7.3)
        id UAA24149; Mon, 14 Apr 1997 20:37:16 -0400 (EDT)
Resent-Date: Mon, 14 Apr 1997 20:37:16 -0400 (EDT)
Message-Id: <199704150040.UAA11517@anshar.shadow.net>
From: "Rocco Caputo" <troc@shadow.net>
To: "Gary Howland" <gary@systemics.com>,
        "Tom Christiansen" <tchrist@jhereg.perl.com>
Cc: "Gary Howland" <gary@systemics.com>, "Hugo van der Sanden" <hv@iii.co.uk>,
        "hv@tyree.iii.co.uk" <hv@tyree.iii.co.uk>,
        "perl5-porters@perl.org" <perl5-porters@perl.org>
Date: Mon, 14 Apr 97 20:34:01 -0500
Reply-To: "Rocco Caputo" <troc@shadow.net>
Priority: Normal
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: Re: Perl5.005 wish list (event loop)
Resent-Message-ID: <"99mWD.A.PzF.i0sUz"@sinistar>
Resent-From: perl5-porters@idle.com
X-Mailing-List: <perl5-porters@idle.com> archive/latest/6171
X-Loop: perl5-porters@idle.com
Precedence: list
Resent-Sender: perl5-porters-request@idle.com
Content-Type: text/plain; charset="iso-8859-1"
Content-Length: 1119
Status:   

Gary, et al,

Almost a year ago, I quietly announced something called "Serv + Face".
Maybe my announcement was a little too quiet.

Serv is a fork-less, select-based framework of event server classes.
It provides a high level interface to select(), and a very high level
interface to TCP client and server socket operations.  It does not fork.

Face is the start of a curses-based UI framework that can run alone
or use Serv as its main loop.

The code and a rough draft of the documentation are available from
<http://www.shadow.net/~troc/perlstuff.html>.  If this code is useful
to anyone, I'd sure like to know.

Rocco
<troc@shadow.net>

On Tue, 15 Apr 1997 01:36:35 +0200, Gary Howland wrote:
>
>Select is fine.  What we (the "event evangelists") want is a "level above" 
>select.  When we have a chunk of data to send to x streams, we don't want to 
>have to call select, see which stream is ready for writing, work out how
>many bytes we can send, send those bytes, shorten our buffers by that amount
>of bytes, and loop back to select.  We just want to send the data.  And we
>want to do this without forking.

-------------------------------------------------------------------------------

Received: from sinistar.idle.com (sinistar.idle.com [198.109.160.36])
        by anshar.shadow.net (8.7.3/8.7.3) with ESMTP id JAA04948
        for <troc@shadow.net>; Fri, 7 Feb 1997 09:54:31 -0500 (EST)
Received: (from slist@localhost) by sinistar.idle.com (8.7.5/8.7.3)
        id JAA12519; Fri, 7 Feb 1997 09:00:19 -0500 (EST)
Resent-Date: Fri, 7 Feb 1997 09:00:19 -0500 (EST)
Message-Id: <199702071400.JAA00339@anshar.shadow.net>
From: "Rocco Caputo" <troc@shadow.net>
To: "Felix Gallo" <fgallo@wellspring.us.dg.com>,
        "perl5-porters@perl.org" <perl5-porters@perl.org>
Date: Fri, 07 Feb 97 08:54:31 -0400
Reply-To: "Rocco Caputo" <troc@shadow.net>
Priority: Normal
Subject: polytheistic perl references
Resent-Message-ID: <"1y3hHB.A.w5C.sTz-y"@sinistar>
Resent-From: perl5-porters@perl.org
X-Mailing-List: <perl5-porters@perl.org> archive/latest/136
X-Loop: perl5-porters@perl.org
Precedence: list
Resent-Sender: perl5-porters-request@perl.org
Content-Type: text
Content-Length: 1502
Status:   

On Thu, 06 Feb 1997 12:52:56 +0000, Felix Gallo wrote:

>Felix's Perl-related Metaproblems:
>
>3.  Perl references are monotheistic.  One fancies that saying
>$x = \{ http://perl.com/myperlobject }; would do the right thing,
>but the established structure of Perl seems to make this difficult.

There are tied hash packages that implement object naming
and message passing between named objects within the same
process.  The packages allow invocations like:

  $msg{'desktop,paint'} = 1;
  $msg{'name entry,value'} = 'J. K. Cohen';
  $active_flag = $msg{'active checkbox,value'};

The packages also do broadcasting to subsets of the object
dictionary.  Hash stores and fetches are sent to or taken
from all the objects that match the supplied name.  So to
clear the value of all objects that have 'entry' in their
names:

  $msg{'entry,value'} = '';

That clears 'name entry' and 'age entry' and 'salary entry'
and ....

Anyway, the names could be extended to work across sockets
in the presence of a standard select/event loop:

  $gnats_queue = $msg{'//somewhere.com:4242/stickynote/gnat?count'};
  print "gnat has $gnats_queue unread sticky notes.\n";

  $message = 'hello, world!';
  $msg{'//somewhere.org:4242/stickynote/merlyn?queue'} = $message;

Man pages for ObjDict::Names and ObjDict::Messages are
on-line at <http://www.nexi.com/troc>.  The code is inside
a larger package, Serv+Face, at
<http://www.shadow.net/~troc/perlstuff.html>.

Just some ideas to ponder.

Rocco
<troc@shadow.net>

-------------------------------------------------------------------------------

This is a header from a program I was writing before I discovered Perl.

// =========================================================================
//  UBERSYS.H
//   UberSys definitions and classes.
// =========================================================================

#include <io.h>
#include <dir.h>
#include <dos.h>
#include <math.h>
#include <time.h>
#include <alloc.h>
#include <conio.h>
#include <ctype.h>
#include <fcntl.h>
#include <share.h>
#include <stdio.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <values.h>
#include <fstream.h>
#include <iomanip.h>
#include <iostream.h>
#include <sys\stat.h>

// -------------------------------------------------------------------------
// Constants, limits, and the like.

#define SIZE_UID        9                       // including NULL terminator
#define SIZE_PWD        26                      // including NULL terminator
#define SIZE_MAXSTR     0x1000                  // 4k string sizes (max)
#define SIZE_MAXPATH    0x0050                  // 160 chars for max path
#define SIZE_MAXLINE    0x00A0                  // 160 characters per line
#define COUNT_LINES     0x0200                  // 512 editor lines

#define USREV           0x0200                  // version 02.00

#define DRV             "D:"                    // drive it runs on

// -------------------------------------------------------------------------
// Helper macros.
                                        // build a 20-bit address from segoff
#define A20(x)  (((ULI)FP_SEG(x)<<4)+(ULI)FP_OFF(x))
                                        // make a normalized pointer from A20
#define A32(x)  MK_FP((UINT)((x)>>4), (UINT)((x)&0x0F))
                                        // normalize a far pointer using A20
#define NORM(x) A32(A20(x))
                                        // maximum of two values
template <class T>
T max(T x, T y)
{
        return((x>y)?x:y);
};
                                        // minimum of two values
template <class T>
T min(T x, T y)
{
        return((x<y)?x:y);
};
                                        // inline assembly shorthand
#define I asm

#define FATAL fatalerr(thisFile,__LINE__)
#define FATALH(x) fatalerr(x,__LINE__)

#if defined(DEBUG)
#   define ERRS             if(errorstream)*errorstream<<setiosflags(ios::uppercase)<<hex
#   define CRV(x)           if((x)==RV_FAILURE)FATAL
#   define CNE(x)           if((x)==-1)FATAL
#   define CZE(x)           if(!(x))FATAL
#   define FLINE            ,thisFile,__LINE__
#   define FLINC            thisFile,__LINE__
#   define FLINP            , char *file, int line
#   define FLINQ            char *file, int line
#   define FLINI            ,file,line
#   define FLINJ            file,line
#   define FLINS            thisFile,__LINE__,
#   define FLINT            char *file, int line,
#   define FLI              dec<<");\t\t// f:"<<setw(12)<<file<<" @ l:"<<setw(4)<<line
#   define BOTH(x)          A20(x)<<" ["<<A20(*(x))<<"]"
#   define DEB(x)           x
#   define DEB2(x,y)        x,y
#   define NEW              ERRS<<dec<<"\nCall to new.\t\t\t\t\t// f:"<<setw(12)<<thisFile<<" @ l:"<<setw(4)<<__LINE__;
#   define DEL              ERRS<<dec<<"\nCall to delete.\t\t\t\t\t// f:"<<setw(12)<<thisFile<<" @ l:"<<setw(4)<<__LINE__;
#   define WHEREAMI         ERRS<<dec<<"\nInside file "<<thisFile<<" @ line "<<__LINE__;
#   define ORPHANS          { ERRS<<dec<<"\nOrphan check in "<<thisFile<<" @ line "<<__LINE__<<". "; CRV(aOrphans(FLINC)); }
#   define VALID(dp)        CZE(aValid(aHeader(dp)));
#   define DUMP             aDump(FLINC);
#else
#   define ERRS             cerr
#   define CRV(x)           x
#   define CNE(x)           x
#   define FLINE
#   define FLINC
#   define FLINP
#   define FLINQ            void
#   define FLINI
#   define FLINJ
#   define FLINS
#   define FLINT
#   define DEB(x)
#   define DEB2(x,y)
#   define NEW
#   define DEL
#   define WHEREAMI
#   define ORPHANS
#   define VALID(dp)
#   define DUMP
#endif

#define FALSE 0
#define TRUE (~FALSE)

// -------------------------------------------------------------------------

void fatalerr(char *file, int line);

extern char *buildbuf;

// -------------------------------------------------------------------------
                                        // Paradox Engine header
#include "pxengine.h"
                                        // Error stream if debugging.
DEB(extern ofstream *errorstream;)
                                        // Message file header.
#include "general.h"
                                        // Type definitions.
#include "mytypes.h"
                                        // Database functions.
#include "pxe.h"
#include "users.h"
#include "ipx.h"
                                        // Code groups.
#include "pcodes.h"
#include "gsbl.h"
#include "arena.h"
#include "interrup.h"
#include "port.h"
#include "msgfile.h"
#include "task.h"
#include "tam.h"
#include "qualpath.h"
#include "xmm.h"
#include "var.h"
#include "safepxi.h"
#include "template.h"
#include "token.h"
#include "stack.h"
#include "objfile.h"
#include "ofm.h"
#include "srcfile.h"
#include "pmachine.h"
                                        // BBS modules.
#include "hangup.h"
#include "idle.h"
#include "login.h"
#include "editor.h"
#include "cmdline.h"
#include "console.h"
#include "dirlist.h"
#include "compiler.h"
#include "disasm.h"
#include "runtime.h"

// -------------------------------------------------------------------------

extern TAM *tam;

-------------------------------------------------------------------------------

Light was let be.