[comp.os.msdos.programmer] Multi-thread on Ms-dos

pipkinsj@cpqhou.uucp (Jeff Pipkins @Adv Dev@SE hou ) (03/22/91)

In article <104499@unix.cis.pitt.edu> bill@hpb.cis.pitt.edu (Bill Broadly) writes:
>
>	I need a small C program that allows running more that
>one thread, using setjmp, and longjmp, on a ms-dos machine.  Nothing 
>pre-emptive needed.  
>	Exactly what I wanted was in the Jan issue of C users Journal,
>but I couldn't get it to work, and the author is in England (no Email
>address).
>	Anybody out there have such a beast?  Or get the one from the
>magazine to work?  
>	I don't want anything so involved as Ctask.  I don't need real
>multitasking just the ability for one process to say "I done for now,
>next process can run".
>	I will happily mail the code to anyone if they want the
>original (non-working) code from the article.
>	I have (bought) MSC 6.0 (with revision), MSC 5.1, TC++.
>			-Bill
>			Broadley@schneider3.lrdc.pitt.edu
>
>-- 
>Bill Broadley
>Broadley@schneider3.lrdc.pitt.edu
>			"GISMO"

This is a nice thought, but it doesn't work.  I actually tried this scenario,
but the problem is that all threads have the same stack.  As soon as any
one of them do a longjmp(), it throws away a piece of the stack.  Then
the next thread writes over the values that were written there when it
uses the stack.  When control gets back to the first thread, it can't
continue where it left off because much of its stack, containing local
variables, function arguments, and return addresses, is gone forever.

There are certain very simple situations in which it seems to work, usually
in a program called test.c  :)   If you test this method in such a way
that the stack level is the same from one thread to another, then it will
work by happenstance.  But if you use it for any real work at all, then
it will fail.  Note that even if you try to design the threads so that
the stack levels are the same, that doesn't guarantee that it will work,
because the compiler may allocate different amounts of stack space for
each thread to use as temporaries in evaluating expressions.

The fundamental truth here is this: you MUST have a separate stack for
each thread.  There is no way around this.

There is a way to do something similar to coroutines with setjmp/longjmp,
but it is rather delicate and unelegant.

Good idea, but it just doesn't work...

-- 
Jeff D. Pipkins  (pipkinsj@cpqhou.se.hou.COM  or  uunet!cpqhou!pipkinsj)

Disclaimer: My opinions do not necessarily reflect those of my employer.
"Things should be made as simple as possible, but no simpler" --Einstein

cadsi@ccad.uiowa.edu (CADSI) (03/23/91)

From article <1991Mar22.155529.22@cpqhou.uucp>, by pipkinsj@cpqhou.uucp (Jeff Pipkins @Adv Dev@SE hou ):
> In article <104499@unix.cis.pitt.edu> bill@hpb.cis.pitt.edu (Bill Broadly) writes:
>>
> 
> This is a nice thought, but it doesn't work.  I actually tried this scenario,
> but the problem is that all threads have the same stack.  As soon as any
> one of them do a longjmp(), it throws away a piece of the stack.  Then
> the next thread writes over the values that were written there when it
> uses the stack.  When control gets back to the first thread, it can't
> continue where it left off because much of its stack, containing local
> variables, function arguments, and return addresses, is gone forever.
> 
> There are certain very simple situations in which it seems to work, usually
> in a program called test.c  :)   If you test this method in such a way
> that the stack level is the same from one thread to another, then it will
> work by happenstance.  But if you use it for any real work at all, then
> it will fail.  Note that even if you try to design the threads so that
> the stack levels are the same, that doesn't guarantee that it will work,
> because the compiler may allocate different amounts of stack space for
> each thread to use as temporaries in evaluating expressions.
> 
> The fundamental truth here is this: you MUST have a separate stack for
> each thread.  There is no way around this.
> 
> There is a way to do something similar to coroutines with setjmp/longjmp,
> but it is rather delicate and unelegant.

Actually, if you look through the back issues of Computer Language magazine,
John Du Glosz (sp?) presented an article that did a similar procedure.
He created stack arrays for each thread, and switched stacks as new threads
came into scope.  HOWEVER, he did not implement preemptive tasking, just
Yield type calls.  His algorithm is easily upgraded though.

|----------------------------------------------------------------------------|
|Tom Hite					|  The views expressed by me |
|Manager, Product development			|  are mine, not necessarily |
|CADSI (Computer Aided Design Software Inc.	|  the views of CADSI.       |
|----------------------------------------------------------------------------|

markh@csd4.csd.uwm.edu (Mark William Hopkins) (03/29/91)

In article <104499@unix.cis.pitt.edu> bill@hpb.cis.pitt.edu (Bill Broadly) writes:
>	I need a small C program that allows running more that
>one thread, using setjmp, and longjmp, on a ms-dos machine.  Nothing 
>pre-emptive needed.  

In article <1991Mar22.155529.22@cpqhou.uucp> pipkinsj@cpqhou.UUCP (Jeff Pipkins @Adv Dev@SE hou ) writes:
>This is a nice thought, but it doesn't work.  I actually tried this scenario,
>but the problem is that all threads have the same stack.  As soon as any
>one of them do a longjmp(), it throws away a piece of the stack.

You'll have to elaborate on this one.  The longjmp() routine saves the current
stack pointer, and whether a part of the stack is thrown away or not, when
you start up the next thread with another longjmp() the stack pointer is
switched there over to another completely different part of the stack segment
before the previous part of the stack segment has had a chance to get corrupted.

>There are certain very simple situations in which it seems to work, usually
>in a program called test.c  :)

I actually got test.c to switch data segments under certain cases too thus
allowing rudimentary swapping. :)

>There is a way to do something similar to coroutines with setjmp/longjmp,
>but it is rather delicate and unelegant.

In the Empire Strikes Back, to Obi-Wan's lament "That boy is our last hope",
Yoda said "No, there is another...".  Don't leave us waiting 3 years. :)

resnicks@netcom.COM (Steve Resnick) (03/30/91)

In article <10592@uwm.edu> markh@csd4.csd.uwm.edu (Mark William Hopkins) writes:
>In article <104499@unix.cis.pitt.edu> bill@hpb.cis.pitt.edu (Bill Broadly) writes:
>>	I need a small C program that allows running more that
>>one thread, using setjmp, and longjmp, on a ms-dos machine.  Nothing 
>>pre-emptive needed.  
>
>In article <1991Mar22.155529.22@cpqhou.uucp> pipkinsj@cpqhou.UUCP (Jeff Pipkins @Adv Dev@SE hou ) writes:
>>This is a nice thought, but it doesn't work.  I actually tried this scenario,
>>but the problem is that all threads have the same stack.  As soon as any
>>one of them do a longjmp(), it throws away a piece of the stack.
>
>You'll have to elaborate on this one.  The longjmp() routine saves the current
>stack pointer, and whether a part of the stack is thrown away or not, when
>you start up the next thread with another longjmp() the stack pointer is
>switched there over to another completely different part of the stack segment
>before the previous part of the stack segment has had a chance to get corrupted.
>

If you don't switch stacks, anything which uses a reasonable amount of 
stack space will get munged pretty badly. I wrote a multi-thread thingy 
with Turbo C and assembler. The assembly piece is used to launch the new
routine with a new stack. The assembler (for large model code) is:

-------------------- Cut Here ---------- Cut Here --------------------------
.model 	large
EXTRN	_EndTask:FAR
.code
PUBLIC	_StartTask
;
; Usage: StartTask( void far (*entrypt)(void),char far * Stack)
;
; StartTask sets up a new stack for a particular task, and then calls the
; task entry point. If the newly created task returns, EndTask() is called
; to invalidate the current task, and no more processing occurs.
;
_StartTask	PROC
	PUSH	BP
	MOV	BP,SP			; Save current stack frame
	MOV	AX,[BP+12]		; Segment Address of New Stack
	MOV	BX,[BP+10]		; Offset Address of New Stack
	MOV	CX,[BP+8]		; Segment Address of entry point
	MOV	DX,[BP+6]		; Offset Address of entry point
	MOV	SS,AX			; Select new stack segment 
	MOV	SP,BX			; And set new stack pointer
	MOV	AX,offset CEtsk		; Get IP Value for dummy return
	PUSH	CS			; Make return segment point to here
	PUSH	AX			; Make return offset  point to CEtsk
	PUSH	CX			; Push return CS
	PUSH	DX			; Push Return IP
	RET
CEtsk:	CALL	far ptr _EndTask	; EndTask is a routine which 
					; invalidates the current thread.
_StartTask	ENDP
	END
	

-----------------------------------------------------------------------------
Have fun! :)
Steve
 	

-- 
-------------------------------------------------------------------------------
	resnicks@netcom.com, steve@camphq, IFNA:	1:143/105.0, 
USNail: 530 Lawrence Expressway, Suite 374 
        Sunnyvale, Ca 94086
- In real life: Steve Resnick. Flames, grammar and spelling errors >/dev/null
0x2b |~ 0x2b, THAT is the question.
The Asylum OS/2 BBS - (408)263-8017 12/2400,8,1 - Running Maximus CBCS 1.2
-------------------------------------------------------------------------------