[comp.lang.forth] Multi-user Forth

toma@tekgvs.TEK.COM (Tom Almy) (07/29/87)

In the recent deluge of postings, I seem to remember someone asking about
multitasking/multiuser Forths.  I have modified a copy of the public domain
F83 (by Laxen and Perry) for multiuser (it is normally just multitasking).
I will supply it (sources included, of course) to anyone who requests it.

I did the modifications several years ago on the 8080 version 1.0, so anyone
wanting multiuser on the 8086 version will have to examine the sources and
make the appropriate modification to the 8086 version.  I ran the program
with three users on a Lobo MAX-80 (5 Mhz Z-80, 8" DSDD floppies, and 256k
ram disk) with excellent performance.  The program should work on an IBM PC
if UNIDOS is used, with the only necessary mods being serial port access.

Important details:
	The FORTH vocabulary "forks" so that each user additions to FORTH
are private to his/her tasks.  Other vocabularies existing before switching
to multiuser are shared, allowing shared variables between users.
	Sufficient variables have been converted to user variables so that
each user can edit separate screen files, and can have different terminal
definitions.  The editor has been modified for much more efficient display
update (very important when running multiuser or over serial ports).
	A PolyForth "TYPIST" like task is provided.  The typist can be 
considered as another user task which can receive character input from any
user and sends all output to the printer.  To use, type a line "TYPIST: <commands>".
If the typist is busy, you will be told so, and you can wait or hit a key
to abort the (queued) command.  When the typist is ready to accept the
command, your task resumes.  If the typist encounters any error, the error
will be reported back to your terminal!
	A multitasking demo is provided -- Tower of Hanoi using message
passing among a dozen tasks rather than recursion.  This demo can be executed
by all users simultaneously!  (A problem: FORGET needs to be modified so
as to delete any forgotten tasks, currently it leaves them chained in and
the system will soon crash after forgetting).
	(Totally irrelevent to this discussion) the words INSTANCES_OF and
USERS_OF are provided which do entire vocabuary (or all vocabulary) searches
for words which either use a given word or are instances of (i.e. CREATE DOES>)
a given word.  I got the idea for this from the Smalltalk browser.

Reply to me if interested.  I could try emailing it, or can USmail it if sent
a Self Addressed Stamped Mailer and a floppy (IBM PC Format only!!).

Tom Almy
toma@tekgvs.TEK.COM
{most hubs}!tektronix!tekgvs!toma

rat@circle.UUCP (08/14/87)

I would think that task-switching during NEXT would create too high of an 
overhead. Seems to me that switching during : or ; would be more 
reasonable.  However, I would still prefer PAUSE and similar for more 
control.
 
However, I would like to be able to have a multitasking Forth which can 
handle having more than one file open at a time; this is with a 
single-user operating system (namely Prodos, but MS-DOS or CP/M are also 
single-user similarly). How in tarnation would you keep track of the open 
files?
 
The big problem is that I want two users to be able to write in the same 
file at the same time; so I need record-locking but dont know how to 
implement it. Any thoughts?
 
By the way, I would say that there are SOME of us STILL using CP/M out 
here... too bad the F83 isnt Z80 code though. Better yet, somebody otta 
write a decent F83 for ZCPR3.
 
       "Curiouser and curiouser," said Alice.
 


--  
::: David Douthitt ::: Madison, Wisc ::: 
uucp mail: ...!uwvax!geowhiz!uwspan!hobbes!circle!rat
fidonet mail: 121/1

toma@tekgvs.TEK.COM (Tom Almy) (08/18/87)

In article <9.2122EF36@circle.UUCP> rat@circle.UUCP (David Douthitt) writes:
>I would think that task-switching during NEXT would create too high of an 
>overhead. Seems to me that switching during : or ; would be more 
>reasonable.  However, I would still prefer PAUSE and similar for more 
>control.

The scheme used in the Multi User Forth that I posted last week changes on
"PAUSE". (This is F83)

> 
>However, I would like to be able to have a multitasking Forth which can 
>handle having more than one file open at a time; this is with a 
>single-user operating system (namely Prodos, but MS-DOS or CP/M are also 
>single-user similarly). How in tarnation would you keep track of the open 
>files?

Each task has a file number as one of its user variables.  So each user can 
do a "1 LIST" simultaneously and yet each may be displaying a different 
screen!  The block buffers' headers have both the block number and file number
and the LRU assignment algorith works over all users-- the buffer pool is
shared rather than assigned.  For smoothest operation there should be at 
least as many buffers as users, but it is not required (ever see disk 
thrashing?).

> 
>The big problem is that I want two users to be able to write in the same 
>file at the same time; so I need record-locking but dont know how to 
>implement it. Any thoughts?

Yes, use semaphores on the block buffers, and add a new word, RELEASE,
to signify release of the block buffer.  Change the block allocation
algorithm to skip locked block buffers.  Of course, if the task can get
all of its work done without doing anything that causes a PAUSE, then
you are home free without going to this trouble.  

USER VARIABLE curblk

: LOCKINGBLOCK  DUP curblk !  
	BLOCK ( the old code )
	DUP { offset to header semaphore location } SEMA ;

: RELEASE  curblk @ BLOCK { offset to header semaphore location } PHORE ;

Where SEMA and PHORE are implemented as:

: SEMA ( locationToLock -- )
	BEGIN DUP @ WHILE PAUSE REPEAT  
	ON ;

: PHORE ( locationToRelease -- )
	OFF ;


 
>By the way, I would say that there are SOME of us STILL using CP/M out 
>here... too bad the F83 isnt Z80 code though. Better yet, somebody otta 
>write a decent F83 for ZCPR3.

I recently stopped using a Z80, but the F83 code can be Z80ized.
The inner interpreter address should be put in IX (or IY) and JMP [IX]
used to get to it.  The multiply and divide primitives can be rewritten
to get MUCH better performance, and of course CMOVE can be improved.  The
improvement in speed won't be anything to write home about, though.  The
best bet for speed improvement is to change the implementation to direct
threaded code.

Tom Almy
Tektronix, Inc.
toma@tekgvs.TEK.COM

(Standard disclaimer applies)

willner@cfa.harvard.EDU (Steve Willner) (08/19/87)

> In article <9.2122EF36@circle.UUCP> rat@circle.UUCP (David Douthitt) writes:
> >I would think that task-switching during NEXT would create too high of an 
> >overhead. Seems to me that switching during : or ; would be more 
> >reasonable.  However, I would still prefer PAUSE and similar for more 
> >control.
> 
In article <2573@tekgvs.TEK.COM>, toma@tekgvs.TEK.COM (Tom Almy) writes:
> The scheme used in the Multi User Forth that I posted last week changes on
> "PAUSE". (This is F83)

Changing only on PAUSE certainly makes it easier to write code, but
it is far too restrictive in an instrument control application where
a task might be time-critical.  The alternative is to _patch_ either
; or NEXT.  Most of the time the word just executes an extra no-op,
but when an interrupt schedules a new task, the interrupt service
routine changes the no-op to be a jump to the priority-checking word
(PAUSE in many systems), which patches the no-op back in and then
gives control to the highest priority process that is ready to run.
Of course this scheme isn't needed if _all_ the time-critical
processing can be done within the interrupt service routine, but that
condition greatly restricts the amount of processing that can be
done.  The price one pays for the extra capability, of course, is
that code must be carefully written to be interruptible; semaphores
must be used frequently (and in the right order!), global variables
and pointers must be maintained in a fully consistent manner, etc.
The benefit is that a real-time process can proceed unaffected by
user activities.
-- 
Steve Willner              Phone 617-495-7123        Bitnet: willner@cfa1
60 Garden St.              FTS:      830-7123         UUCP:   willner@cfa
Cambridge, MA 02138 USA  Telex:  921428 satellite cam

andrew@teletron.UUCP (Andrew Scott) (08/20/87)

In article <9.2122EF36@circle.UUCP>, rat@circle.UUCP (David Douthitt) writes:
> I would think that task-switching during NEXT would create too high of an 
> overhead. Seems to me that switching during : or ; would be more 
> reasonable.  However, I would still prefer PAUSE and similar for more 
> control.

There must be some misunderstanding of my explanation of task switching
during NEXT which I described in a previous posting.  We don't switch at
every NEXT (which would be incredibly inefficient), only when we want to.
Normal PAUSE is still used to relenquish control to another process, however
we can patch NEXT to perform a PAUSEish task switch when special situations
arise.  For example, should some active task fail to relenquish control through
PAUSE for any reason, a clock interrupt could limit a time slice to some
maximum value and force a PAUSE.  Also, this mechanism could be used to
synchronize some task with an external interrupt.  NEXT is a more convenient
place to perform the task switch than : or ; because it will be executed
much sooner.  If NEXT is implemented as an indirect call (e.g. jsr (a0), where
the a0 register holds the address to NEXT), the interrupt code merely has to
patch the register with the task switching routine's address.  This code can
restore the register to point to NEXT when it's finished the task switch.

As for concerns (expressed by some others) that this technique would be
impractical when dealing with disk buffers etc., I never claimed that this
was a good technique in a multi-USER environment, but a multi-TASKING
environment.  There is a difference!  FORTH is great for real-time systems,
and working with interrupts is a must.  This technique is an easy way to
interface processor interrupts with FORTH tasks.

	Andrew

tenney@well.UUCP (Glenn S. Tenney) (08/21/87)

I believe that "you" must understand that there is a difference between
multitasking and interrupts.  A hi speed time-critical routine really should
be an interrupt routine written in hi level or assembler.  By having the
context switch occur at PAUSE (which, btw, is implied at any I/O type
word such as BLOCK) a whole slew of problems disappear.  It then becomes
the programmer's responsibility to put PAUSE in lot's of extra places
when you think you're going to be doing something cpu intensive AND when
you know it's safe.  It's also your job to be sure things are safe before
ANY I/O word.

All in all, multitasking with PAUSE is much nicer, but it is not what most
CS people are used to since there is no time slicing.  There are MANY
time critical applications running tons of software on lots of terminals etc.
using just such a multitasker.

Btw, such a multitasker can be written entirely in hi level.  I did it
to test things out about 4 years ago (or was it 5?).

Glenn Tenney