home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 200-299 / ff258.lzh / Suplib / doc / lwp.doc < prev    next >
Text File  |  1989-10-18  |  12KB  |  357 lines

  1.  
  2.                  LWP.DOC V1.00
  3.                18 December 1988
  4.  
  5.             LIGHT WEIGHT PROCESSES
  6.  
  7.     Light weight processes are a useful tool when you need modularity but
  8. cannot afford the overhead (in memory and efficiency) of running each
  9. module as a separate task or process.  Specifically, the idea is to
  10. implement a domain such that one could place several dozen functional
  11. modules each in its own LWP.  Further, the whole idea to LWPs is that
  12. the context switch between them take a minimal amount of time.
  13.  
  14.     LWPs as implemented here can do a synchronous context switch in 10
  15. instructions (includes 2 MOVEM instructions).  No asynchronous mechanism
  16. (preemption) exists.  This makes programming much easier and allows one to
  17. utilize the non-reentrant c.lib and other link libraries from LWPs.
  18.  
  19.     Implementation and usage is also incredibly simple.  Starting up an
  20. LWP is about the same as calling a procedure.
  21.  
  22.                    FEATURES
  23.  
  24.     * LWP startup is a function performed by the subroutine rather than
  25.       the routine that calls the subroutine (the subroutine makes itself
  26.       into an LWP).  Usage is very simple.  Recursive and dynamic creation
  27.       of LWPs is inherent in the system.
  28.  
  29.     * Thus, one can call a subroutine several times to create several LWPs
  30.       running the same code, but with different stack contexts.
  31.  
  32.     * The LWP is automatically removed and the stack and descriptors freed
  33.       when an LWP process return()s.
  34.  
  35.     * An alerter and waiter function exists for each LWP.  Any LWP may
  36.       alert any other, and any LWP may WaitLWP() to be alerted.  Non LWP's
  37.       may alert LWPs.
  38.  
  39.       V1.03 and beyond:  AlertLWP() may now be called by another task or
  40.       any kind of interrupt.
  41.  
  42.     * You can dynamically change the stack size of an LWP.  As this
  43.       reallocates the stack one must be careful about throwing around
  44.       addresses of local variables.  The contents of local variables are
  45.       not lost.  This is done by ForkLWP()ing a child then deleting the
  46.       parent, so the LWP descriptor will change too.
  47.  
  48.     * No restrictions on register variables (the Alpha version had
  49.       restrictions).
  50.  
  51.               LIMITATIONS & RESTRICTIONS
  52.  
  53.     * These calls are implemented for C compilers which always have a
  54.       link/unlk pair in their procedures, and use A5 for the link register.
  55.       As far as I know, Aztec C and Lattice C fall into this category if
  56.       you do not use the optimization options.
  57.  
  58.     * Each LWP gets its own stack.  Those LWPs which make no external calls
  59.       may specify a stack size of 0 (assuming the compiler does not use
  60.       the stack for temporary variable space).    Those LWPs which make
  61.       stdio / DOS calls required at least a 2K stack.  Otherwise, the
  62.       stack size depends on the subroutines an LWP calls.
  63.  
  64.  
  65.       NOTE: IF EXCEPTIONS ARE ALLOWED FOR THE EXEC TASK, ALL LWPs MUST
  66.       HAVE ENOUGH STACK TO BE ABLE TO BE INTERRUPTED BY ONE OR MORE OF
  67.       THEM.
  68.  
  69.       SPECIFICALLY, Lattice C uses exceptions to handle ^C (I think)...
  70.       this would have to be disabled.
  71.  
  72.                 USAGE
  73.  
  74.         Please refer to the example program lwptest.c
  75.  
  76.     Here is an example:
  77.  
  78.     main()
  79.     {
  80.     void lwp();
  81.  
  82.     lwp("#A", 4L);
  83.     lwp("#B", 6L);
  84.     lwp("#C", 8L);
  85.     puts("main: beginning run");
  86.     RunLWP();
  87.     }
  88.  
  89.     void
  90.     lwp(str, n)
  91.     char *str;
  92.     long n;
  93.     {
  94.     extern long ThisLWP;    /*  'current' LWP   */
  95.     long i;
  96.             /*
  97.              *    a 2K stack (because we are calling printf), and 8
  98.              *    bytes (two longwords) of arguments.  Warning:
  99.              *    interpretation of char and word arguments depends
  100.              *    on integer size ... they are 4 bytes on the stack
  101.              *    when using 32 bit ints, 2 bytes when using 16 bit
  102.              *    ints.  Best to pass longs, but it is up to you.
  103.              *
  104.              *    If you specify 0 for the second argument (instead
  105.              *    of 8), then no access to arguments passed to this
  106.              *    subroutine exists after the InitLWP() call.  Since
  107.              *    you are running in the initial stack context before
  108.              *    the InitLWP() call, you *can* use the arguments.
  109.              *    Note thus that before the InitLWP() call you *do*
  110.              *    have access to the main stack and can thus make
  111.              *    calls which use a lot of stack.
  112.              *
  113.              *    If you specify 0 for the stack size, only enough
  114.              *    stack for this subroutine will be allocated (local
  115.              *    variables and such).  Other values specify the
  116.              *    excess stack beyond this minimum that you want.
  117.              */
  118.  
  119.     if (ForkLWP(2048, 8)) {
  120.         printf("Parent's LWP = %08lx (should be 0)\n", ThisLWP);
  121.         printf("child %s queued\n", str);
  122.         return;           /* parent returns, child is queued  */
  123.     }
  124.     printf("Child's LWP = %08lx (should be non-zero)\n", ThisLWP);
  125.     printf("child %s running\n", str);
  126.  
  127.     for (i = 0; i <= n; ++i) {
  128.         printf("lwp %s %ld/%ld\n", str, i, n);
  129.         SwitchLWP();           /* let other LWPs run */
  130.     }
  131.     /* return automatically unlinks and frees memory associated with the
  132.      * lwp descriptor and stack
  133.      */
  134.     }
  135.  
  136.     1> main
  137.     Parent's LWP = 00000000 (should be 0)
  138.     child #A queued
  139.     Parent's LWP = 00000000 (should be 0)
  140.     child #B queued
  141.     Parent's LWP = 00000000 (should be 0)
  142.     child #C queued
  143.     main: beginning run
  144.     Child's LWP = <someadr> (should be non-zero)
  145.     child #C running
  146.     lwp #C 0/8
  147.     Child's LWP = <someadr> (should be non-zero)
  148.     child #B running
  149.     lwp #B 0/6
  150.     Child's LWP = <someadr> (should be non-zero)
  151.     child #A running
  152.     lwp #A 0/4
  153.     lwp #C 1/8
  154.     lwp #B 1/6
  155.     lwp #A 1/4
  156.     lwp #C 2/8
  157.     lwp #B 2/6
  158.     lwp #A 2/4
  159.     lwp #C 3/8
  160.     lwp #B 3/6
  161.     lwp #A 3/4
  162.     lwp #C 4/8
  163.     lwp #B 4/6
  164.     lwp #A 4/4
  165.     lwp #C 5/8
  166.     lwp #B 5/6
  167.     lwp #C 6/8
  168.     lwp #B 6/6
  169.     lwp #C 7/8
  170.     lwp #C 8/8
  171.  
  172.  
  173.     Operation works like this:    main() calls the subroutine to be made
  174.     into an LWP process.  At some point in the beginning the subroutine
  175.     will make a call to ForkLWP(), which allocates and copies its context,
  176.     then returns 0 to the child and non-zero to the 'parent'.  Normally,
  177.     the 'parent' returns back to main().
  178.  
  179.     ForkLWP() allocates a stack large enough to handle the child's
  180.     local variables, arguments, and the extra stack space specified.  If
  181.     you were to specify a stack size of 0 then the subroutine would have
  182.     only enough stack for its local variables and would not be able to
  183.     make other subroutine calls.
  184.  
  185.     ForkLWP() saves the subroutine's stack context and registers onto
  186.     the newly allocated stack and LWP descriptor, then returns a non-zero
  187.     (the LWP descriptor which may be used to AlertLWP() the child) to the
  188.     parent, and 0 to the child.  main() then RunLWP()s
  189.  
  190.     RunLWP() returns whenever there are no LWPs ready to run... either
  191.     when they have all been deleted or all the remaining ones are waiting
  192.     for an event.
  193.  
  194.     Note that new light weight processes may be added at any time, even
  195.     by other LWPs.
  196.  
  197.                WaitLWP()/AlertLWP()/SwitchLWP()
  198.  
  199.  
  200.     When an LWP gets CPU, it must relinquish it to allow the next LWP
  201.     to run.  Calling SwitchLWP() will accomplish this.  SwitchLWP() is
  202.     a fast nop if no other LWPs are ready to run.  Calling WaitLWP() will
  203.     cause the LWP to be removed from the list of LWPs ready to run and
  204.     cause a context switch to the next ready LWP.  (Remember that the
  205.     highlevel call RunLWP() will return whenever there are no LWPs ready
  206.     to run).
  207.  
  208.     The global variable ThisLWP identifies the LWP descriptor (longword)
  209.     for the currently running LWP.  Be very careful about LWP descriptors..
  210.     they become invalid whenever that particular LWP exits.  Also be
  211.     careful about passing pointers around ... when an LWP exits, its
  212.     stack goes away.
  213.  
  214.     extern long ThisLWP;
  215.  
  216.     main()
  217.     {
  218.     master();
  219.     RunLWP();
  220.     }
  221.  
  222.     master()
  223.     {
  224.     long slave();
  225.     long slave_id;
  226.  
  227.     if (ForkLWP(2048, 0))
  228.         return;
  229.     slave_id = slave(ThisLWP);
  230.     puts("master: Waiting for Slave to signal me");
  231.     WaitLWP();
  232.     puts("master: Master Signalling slave to exit & Master exiting");
  233.     AlertLWP(slave_id);
  234.     }
  235.  
  236.     long
  237.     slave(master_id)
  238.     long master_id;
  239.     {
  240.     long slave_id;
  241.     if (slave_id = ForkLWP(2048, 4))
  242.         return(slave_id);
  243.     AlertLWP(master_id);
  244.     puts("slave: alerting master & waiting for master to signal me");
  245.     WaitLWP();
  246.             /* master_id now invalid because it exited */
  247.     puts("slave: master signalled me, exiting");
  248.     }
  249.  
  250.     1> main
  251.     master: Waiting for Slave to signal me
  252.     slave: alerting master & waiting for master to signal me
  253.     master: Master Signalling slave to exit & Master exiting
  254.     slave: master signalled me, exiting
  255.  
  256.  
  257.                 TECHNICAL
  258.  
  259.     As you have seen, one LWP may startup another.    You might ask,
  260.     what happens if an LWP calls ForkLWP() more than once?  The answer
  261.     is that it forks off the subroutine again:
  262.  
  263.     main()
  264.     {
  265.     lwp();
  266.     RunLWP();
  267.     }
  268.  
  269.     lwp()
  270.     {
  271.     short i = 23;
  272.     short j = 0;
  273.  
  274.     if (ForkLWP(2048, 0))
  275.         return;
  276.                 /* when child is initially run by RunLWP */
  277.     ForkLWP(2048, 0);
  278.                 /* there are now TWO LWPs at this pt */
  279.     ++j;
  280.     printf("j had better be one both times, is %d\n", j);
  281.     }
  282.  
  283.     1> main
  284.     j had better be one both times, is 1
  285.     j had better be one both times, is 1
  286.  
  287.               VERY ADVANCED TOPICS
  288.  
  289.     A new function (1.03 and beyond) has been added called AutoAlertLWP().
  290.     The format is:
  291.  
  292.     AutoAlertLWP(port, lwp/NULL)
  293.  
  294.     This function turns on or off automatic alerting of LWPs when a message
  295.     is sent to an EXEC message port.  The port must have mp_SigBit setup
  296.     initially.    Calling with a valid LWP descriptor sets pa_Flags to 3 and
  297.     pa_SigTask to a special handler routine (3 is an undocumented direct
  298.     call feature of ports).  Calling with a NULL LWP descriptor sets
  299.     pa_Flags to 2 (PA_SIGNAL) and ps_SigTask to your task.
  300.  
  301.     While a port is setup for AutoAlert, it will do one of two things:
  302.  
  303.     (1) If LWPs are not running (not within a RunLWP() call), sending
  304.         a message to the port causes the task to get signaled AMD the
  305.         lwp to be alerted to be put on the ready list.
  306.  
  307.     (2) If LWPs are running, sending a message to the port causes the
  308.         LWP to be alerted to be put on the ready list.
  309.  
  310.     AutoAlertLWP() allows one to have a single Wait() call in his main()
  311.     loop, something like:
  312.  
  313.     SetSignal(Mask,Mask);   /*  force first Wait to fall through */
  314.     while (Mask) {
  315.         Wait(Mask);     /*  all signals that are being autoAlerted   */
  316.         RunLWP();       /*  run LWPs until all processing has completed */
  317.     }
  318.  
  319.     Where there is an LWP for every signal in question which does a WaitLWP()
  320.     and then processes all the messages on the port in an endless loop.  Note
  321.     that in the above example, the main loop actually occurs ONLY when the
  322.     program has no processing to do, for if a waiting LWP gets messaged while
  323.     RunLWP() is running, it gets linked into the ready list and RunLWP()
  324.     inherently runs it again.
  325.  
  326.     RESTRICTIONS:   Only one LWP may be applied to a specific signal bit
  327.     at a time.    While you can AutoAlertLWP() several ports to the same LWP,
  328.     you cannot AutoAlertLWP() several ports with the same signal bit to
  329.     different LWPs.  You can AutoAlert() several ports with different
  330.     signal bits to different LWPs.  Understand?
  331.  
  332.     void
  333.     somelwp(port)
  334.     PORT *port;
  335.     {
  336.     long lwp;
  337.     if (lwp = ForkLWP(2048, 4)) {   /*  initialization */
  338.         AutoAlertLWP(port, lwp);
  339.         return;
  340.     }
  341.     for (;;) {
  342.         WaitLWP();
  343.         while (msg = GetMsg(port)) {
  344.         /* optional SwitchLWP() in here if you want a finer
  345.          * illusion of multiple processes.
  346.          */
  347.         }
  348.         /* else you don't need a SwitchLWP() at all w/ the WaitLWP() above */
  349.     }
  350.     }
  351.  
  352.     Note that if unread messages exist on the port when AutoAlertLWP() is
  353.     called, the LWP is immediately alerted (and the task signaled if LWPs
  354.     are not running yet).  If unread messages exist on the port when
  355.     AutoAlertLWP(port, NULL) is called, the task is immediately signaled.
  356.  
  357.