home *** CD-ROM | disk | FTP | other *** search
/ Club Amiga de Montreal - CAM / CAM_CD_1.iso / files / 199.lha / LWP_v1.02 / lwp.doc < prev    next >
Encoding:
Text File  |  1988-12-27  |  8.5 KB  |  274 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 (but not asynchronously ... not from an interrupt,
  38.       another task, or exception).
  39.  
  40.     * You can dynamically change the stack size of an LWP.  As this
  41.       reallocates the stack one must be careful about throwing around
  42.       addresses of local variables.  The contents of local variables are
  43.       not lost.  This is done by ForkLWP()ing a child then deleting the
  44.       parent, so the LWP descriptor will change too.
  45.  
  46.     * No restrictions on register variables (the Alpha version had
  47.       restrictions).
  48.  
  49.               LIMITATIONS & RESTRICTIONS
  50.  
  51.     * These calls are implemented for C compilers which always have a
  52.       link/unlk pair in their procedures, and use A5 for the link register.
  53.       As far as I know, Aztec C and Lattice C fall into this category if
  54.       you do not use the optimization options.
  55.  
  56.     * Each LWP gets its own stack.  Those LWPs which make no external calls
  57.       may specify a stack size of 0 (assuming the compiler does not use
  58.       the stack for temporary variable space).    Those LWPs which make
  59.       stdio / DOS calls required at least a 2K stack.  Otherwise, the
  60.       stack size depends on the subroutines an LWP calls.
  61.  
  62.  
  63.       NOTE: IF EXCEPTIONS ARE ALLOWED FOR THE EXEC TASK, ALL LWPs MUST
  64.       HAVE ENOUGH STACK TO BE ABLE TO BE INTERRUPTED BY ONE OR MORE OF
  65.       THEM.
  66.  
  67.       SPECIFICALLY, Lattice C uses exceptions to handle ^C (I think)...
  68.       this would have to be disabled.
  69.  
  70.                 USAGE
  71.  
  72.         Please refer to the example program lwptest.c
  73.  
  74.     Here is an example:
  75.  
  76.     main()
  77.     {
  78.     lwp("#A", 4L);
  79.     lwp("#B", 6L);
  80.     lwp("#C", 8L);
  81.     puts("main: beginning run");
  82.     RunLWP();
  83.     }
  84.  
  85.     lwp(str, n)
  86.     char *str;
  87.     long n;
  88.     {
  89.     long i;
  90.             /*
  91.              *    a 2K stack (because we are calling printf), and 8
  92.              *    bytes (two longwords) of arguments.  Warning:
  93.              *    interpretation of char and word arguments depends
  94.              *    on integer size ... they are 4 bytes on the stack
  95.              *    when using 32 bit ints, 2 bytes when using 16 bit
  96.              *    ints.  Best to pass longs, but it is up to you.
  97.              *
  98.              *    If you specify 0 for the second argument (instead
  99.              *    of 8), then no access to arguments passed to this
  100.              *    subroutine exists after the InitLWP() call.  Since
  101.              *    you are running in the initial stack context before
  102.              *    the InitLWP() call, you *can* use the arguments.
  103.              *    Note thus that before the InitLWP() call you *do*
  104.              *    have access to the main stack and can thus make
  105.              *    calls which use a lot of stack.
  106.              *
  107.              *    If you specify 0 for the stack size, only enough
  108.              *    stack for this subroutine will be allocated (local
  109.              *    variables and such).  Other values specify the
  110.              *    excess stack beyond this minimum that you want.
  111.              */
  112.  
  113.     if (ForkLWP(2048, 8)) {
  114.         printf("child %s queued\n", str);
  115.         return;           /* parent returns, child is queued  */
  116.     }
  117.     printf("child %s running\n", str);
  118.  
  119.     for (i = 0; i <= n; ++i) {
  120.         printf("lwp %s %ld/%ld\n", str, i, n);
  121.         SwitchLWP();           /* let other LWPs run */
  122.     }
  123.     /* return automatically unlinks and frees memory associated with the
  124.      * lwp descriptor and stack
  125.      */
  126.     }
  127.  
  128.     1> main
  129.     child #A queued
  130.     child #B queued
  131.     child #C queued
  132.     main: beginning run
  133.     child #C running
  134.     lwp #C 0/8
  135.     child #B running
  136.     lwp #B 0/6
  137.     child #A running
  138.     lwp #A 0/4
  139.     lwp #C 1/8
  140.     lwp #B 1/6
  141.     lwp #A 1/4
  142.     lwp #C 2/8
  143.     lwp #B 2/6
  144.     lwp #A 2/4
  145.     lwp #C 3/8
  146.     lwp #B 3/6
  147.     lwp #A 3/4
  148.     lwp #C 4/8
  149.     lwp #B 4/6
  150.     lwp #A 4/4
  151.     lwp #C 5/8
  152.     lwp #B 5/6
  153.     lwp #C 6/8
  154.     lwp #B 6/6
  155.     lwp #C 7/8
  156.     lwp #C 8/8
  157.  
  158.  
  159.     Operation works like this:    main() calls the subroutine to be made
  160.     into an LWP process.  At some point in the beginning the subroutine
  161.     will make a call to ForkLWP(), which allocates and copies its context,
  162.     then returns 0 to the child and non-zero to the 'parent'.  Normally,
  163.     the 'parent' returns back to main().
  164.  
  165.     ForkLWP() allocates a stack large enough to handle the child's
  166.     local variables, arguments, and the extra stack space specified.  If
  167.     you were to specify a stack size of 0 then the subroutine would have
  168.     only enough stack for its local variables and would not be able to
  169.     make other subroutine calls.
  170.  
  171.     ForkLWP() saves the subroutine's stack context and registers onto
  172.     the newly allocated stack and LWP descriptor, then returns a non-zero
  173.     (the LWP descriptor which may be used to AlertLWP() the child) to the
  174.     parent, and 0 to the child.  main() then RunLWP()s
  175.  
  176.     RunLWP() returns whenever there are no LWPs ready to run... either
  177.     when they have all been deleted or all the remaining ones are waiting
  178.     for an event.
  179.  
  180.     Note that new light weight processes may be added at any time, even
  181.     by other LWPs.
  182.  
  183.                WaitLWP()/AlertLWP()/SwitchLWP()
  184.  
  185.  
  186.     When an LWP gets CPU, it must relinquish it to allow the next LWP
  187.     to run.  Calling SwitchLWP() will accomplish this.  SwitchLWP() is
  188.     a fast nop if no other LWPs are ready to run.  Calling WaitLWP() will
  189.     cause the LWP to be removed from the list of LWPs ready to run and
  190.     cause a context switch to the next ready LWP.  (Remember that the
  191.     highlevel call RunLWP() will return whenever there are no LWPs ready
  192.     to run).
  193.  
  194.     The global variable ThisLWP identifies the LWP descriptor (longword)
  195.     for the currently running LWP.  Be very careful about LWP descriptors..
  196.     they become invalid whenever that particular LWP exits.  Also be
  197.     careful about passing pointers around ... when an LWP exits, its
  198.     stack goes away.
  199.  
  200.     extern long ThisLWP;
  201.  
  202.     main()
  203.     {
  204.     master();
  205.     RunLWP();
  206.     }
  207.  
  208.     master()
  209.     {
  210.     long slave();
  211.     long slave_id;
  212.  
  213.     if (ForkLWP(2048, 0))
  214.         return;
  215.     slave_id = slave(ThisLWP);
  216.     puts("master: Waiting for Slave to signal me");
  217.     WaitLWP();
  218.     puts("master: Master Signalling slave to exit & Master exiting");
  219.     AlertLWP(slave_id);
  220.     }
  221.  
  222.     long
  223.     slave(master_id)
  224.     long master_id;
  225.     {
  226.     long slave_id;
  227.     if (slave_id = ForkLWP(2048, 4))
  228.         return(slave_id);
  229.     AlertLWP(master_id);
  230.     puts("slave: alerting master & waiting for master to signal me");
  231.     WaitLWP();
  232.             /* master_id now invalid because it exited */
  233.     puts("slave: master signalled me, exiting");
  234.     }
  235.  
  236.     1> main
  237.     master: Waiting for Slave to signal me
  238.     slave: alerting master & waiting for master to signal me
  239.     master: Master Signalling slave to exit & Master exiting
  240.     slave: master signalled me, exiting
  241.  
  242.  
  243.                 TECHNICAL
  244.  
  245.     As you have seen, one LWP may startup another.    You might ask,
  246.     what happens if an LWP calls ForkLWP() more than once?  The answer
  247.     is that it forks off the subroutine again:
  248.  
  249.     main()
  250.     {
  251.     lwp();
  252.     RunLWP();
  253.     }
  254.  
  255.     lwp()
  256.     {
  257.     short i = 23;
  258.     short j = 0;
  259.  
  260.     if (ForkLWP(2048, 0))
  261.         return;
  262.                 /* when child is initially run by RunLWP */
  263.     ForkLWP(2048, 0);
  264.                 /* there are now TWO LWPs at this pt */
  265.     ++j;
  266.     printf("j had better be one both times, is %d\n", j);
  267.     }
  268.  
  269.     1> main
  270.     j had better be one both times, is 1
  271.     j had better be one both times, is 1
  272.  
  273.  
  274.