home *** CD-ROM | disk | FTP | other *** search
-
- LWP.DOC V1.00
- 18 December 1988
-
- LIGHT WEIGHT PROCESSES
-
- Light weight processes are a useful tool when you need modularity but
- cannot afford the overhead (in memory and efficiency) of running each
- module as a separate task or process. Specifically, the idea is to
- implement a domain such that one could place several dozen functional
- modules each in its own LWP. Further, the whole idea to LWPs is that
- the context switch between them take a minimal amount of time.
-
- LWPs as implemented here can do a synchronous context switch in 10
- instructions (includes 2 MOVEM instructions). No asynchronous mechanism
- (preemption) exists. This makes programming much easier and allows one to
- utilize the non-reentrant c.lib and other link libraries from LWPs.
-
- Implementation and usage is also incredibly simple. Starting up an
- LWP is about the same as calling a procedure.
-
- FEATURES
-
- * LWP startup is a function performed by the subroutine rather than
- the routine that calls the subroutine (the subroutine makes itself
- into an LWP). Usage is very simple. Recursive and dynamic creation
- of LWPs is inherent in the system.
-
- * Thus, one can call a subroutine several times to create several LWPs
- running the same code, but with different stack contexts.
-
- * The LWP is automatically removed and the stack and descriptors freed
- when an LWP process return()s.
-
- * An alerter and waiter function exists for each LWP. Any LWP may
- alert any other, and any LWP may WaitLWP() to be alerted. Non LWP's
- may alert LWPs (but not asynchronously ... not from an interrupt,
- another task, or exception).
-
- * You can dynamically change the stack size of an LWP. As this
- reallocates the stack one must be careful about throwing around
- addresses of local variables. The contents of local variables are
- not lost. This is done by ForkLWP()ing a child then deleting the
- parent, so the LWP descriptor will change too.
-
- * No restrictions on register variables (the Alpha version had
- restrictions).
-
- LIMITATIONS & RESTRICTIONS
-
- * These calls are implemented for C compilers which always have a
- link/unlk pair in their procedures, and use A5 for the link register.
- As far as I know, Aztec C and Lattice C fall into this category if
- you do not use the optimization options.
-
- * Each LWP gets its own stack. Those LWPs which make no external calls
- may specify a stack size of 0 (assuming the compiler does not use
- the stack for temporary variable space). Those LWPs which make
- stdio / DOS calls required at least a 2K stack. Otherwise, the
- stack size depends on the subroutines an LWP calls.
-
-
- NOTE: IF EXCEPTIONS ARE ALLOWED FOR THE EXEC TASK, ALL LWPs MUST
- HAVE ENOUGH STACK TO BE ABLE TO BE INTERRUPTED BY ONE OR MORE OF
- THEM.
-
- SPECIFICALLY, Lattice C uses exceptions to handle ^C (I think)...
- this would have to be disabled.
-
- USAGE
-
- Please refer to the example program lwptest.c
-
- Here is an example:
-
- main()
- {
- lwp("#A", 4L);
- lwp("#B", 6L);
- lwp("#C", 8L);
- puts("main: beginning run");
- RunLWP();
- }
-
- lwp(str, n)
- char *str;
- long n;
- {
- long i;
- /*
- * a 2K stack (because we are calling printf), and 8
- * bytes (two longwords) of arguments. Warning:
- * interpretation of char and word arguments depends
- * on integer size ... they are 4 bytes on the stack
- * when using 32 bit ints, 2 bytes when using 16 bit
- * ints. Best to pass longs, but it is up to you.
- *
- * If you specify 0 for the second argument (instead
- * of 8), then no access to arguments passed to this
- * subroutine exists after the InitLWP() call. Since
- * you are running in the initial stack context before
- * the InitLWP() call, you *can* use the arguments.
- * Note thus that before the InitLWP() call you *do*
- * have access to the main stack and can thus make
- * calls which use a lot of stack.
- *
- * If you specify 0 for the stack size, only enough
- * stack for this subroutine will be allocated (local
- * variables and such). Other values specify the
- * excess stack beyond this minimum that you want.
- */
-
- if (ForkLWP(2048, 8)) {
- printf("child %s queued\n", str);
- return; /* parent returns, child is queued */
- }
- printf("child %s running\n", str);
-
- for (i = 0; i <= n; ++i) {
- printf("lwp %s %ld/%ld\n", str, i, n);
- SwitchLWP(); /* let other LWPs run */
- }
- /* return automatically unlinks and frees memory associated with the
- * lwp descriptor and stack
- */
- }
-
- 1> main
- child #A queued
- child #B queued
- child #C queued
- main: beginning run
- child #C running
- lwp #C 0/8
- child #B running
- lwp #B 0/6
- child #A running
- lwp #A 0/4
- lwp #C 1/8
- lwp #B 1/6
- lwp #A 1/4
- lwp #C 2/8
- lwp #B 2/6
- lwp #A 2/4
- lwp #C 3/8
- lwp #B 3/6
- lwp #A 3/4
- lwp #C 4/8
- lwp #B 4/6
- lwp #A 4/4
- lwp #C 5/8
- lwp #B 5/6
- lwp #C 6/8
- lwp #B 6/6
- lwp #C 7/8
- lwp #C 8/8
-
-
- Operation works like this: main() calls the subroutine to be made
- into an LWP process. At some point in the beginning the subroutine
- will make a call to ForkLWP(), which allocates and copies its context,
- then returns 0 to the child and non-zero to the 'parent'. Normally,
- the 'parent' returns back to main().
-
- ForkLWP() allocates a stack large enough to handle the child's
- local variables, arguments, and the extra stack space specified. If
- you were to specify a stack size of 0 then the subroutine would have
- only enough stack for its local variables and would not be able to
- make other subroutine calls.
-
- ForkLWP() saves the subroutine's stack context and registers onto
- the newly allocated stack and LWP descriptor, then returns a non-zero
- (the LWP descriptor which may be used to AlertLWP() the child) to the
- parent, and 0 to the child. main() then RunLWP()s
-
- RunLWP() returns whenever there are no LWPs ready to run... either
- when they have all been deleted or all the remaining ones are waiting
- for an event.
-
- Note that new light weight processes may be added at any time, even
- by other LWPs.
-
- WaitLWP()/AlertLWP()/SwitchLWP()
-
-
- When an LWP gets CPU, it must relinquish it to allow the next LWP
- to run. Calling SwitchLWP() will accomplish this. SwitchLWP() is
- a fast nop if no other LWPs are ready to run. Calling WaitLWP() will
- cause the LWP to be removed from the list of LWPs ready to run and
- cause a context switch to the next ready LWP. (Remember that the
- highlevel call RunLWP() will return whenever there are no LWPs ready
- to run).
-
- The global variable ThisLWP identifies the LWP descriptor (longword)
- for the currently running LWP. Be very careful about LWP descriptors..
- they become invalid whenever that particular LWP exits. Also be
- careful about passing pointers around ... when an LWP exits, its
- stack goes away.
-
- extern long ThisLWP;
-
- main()
- {
- master();
- RunLWP();
- }
-
- master()
- {
- long slave();
- long slave_id;
-
- if (ForkLWP(2048, 0))
- return;
- slave_id = slave(ThisLWP);
- puts("master: Waiting for Slave to signal me");
- WaitLWP();
- puts("master: Master Signalling slave to exit & Master exiting");
- AlertLWP(slave_id);
- }
-
- long
- slave(master_id)
- long master_id;
- {
- long slave_id;
- if (slave_id = ForkLWP(2048, 4))
- return(slave_id);
- AlertLWP(master_id);
- puts("slave: alerting master & waiting for master to signal me");
- WaitLWP();
- /* master_id now invalid because it exited */
- puts("slave: master signalled me, exiting");
- }
-
- 1> main
- master: Waiting for Slave to signal me
- slave: alerting master & waiting for master to signal me
- master: Master Signalling slave to exit & Master exiting
- slave: master signalled me, exiting
-
-
- TECHNICAL
-
- As you have seen, one LWP may startup another. You might ask,
- what happens if an LWP calls ForkLWP() more than once? The answer
- is that it forks off the subroutine again:
-
- main()
- {
- lwp();
- RunLWP();
- }
-
- lwp()
- {
- short i = 23;
- short j = 0;
-
- if (ForkLWP(2048, 0))
- return;
- /* when child is initially run by RunLWP */
- ForkLWP(2048, 0);
- /* there are now TWO LWPs at this pt */
- ++j;
- printf("j had better be one both times, is %d\n", j);
- }
-
- 1> main
- j had better be one both times, is 1
- j had better be one both times, is 1
-
-
-