[comp.os.msdos.programmer] Multi-Threaded C

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

There seems to have been a bit of discussion about multi-threaded C.
Having written some code for this as an experiment, I find I have no 
real need for it :) (But give me a preemptive scheduler and I am happy =])

If anyone is interested, here it is. This seems to work both with Turbo C,
and MSC 6.0 (under OS/2). The OS/2 part seems utterly useless, but what
the heck ....
Have fun :) 

-----------------------------------------------------------------------------
#!/bin/sh
# This is a shell archive (produced by shar 3.49)
# To extract the files from this archive, save it to a file, remove
# everything above the "!/bin/sh" line above, and type "sh file_name".
#
# made 04/02/1991 21:22 UTC by resnicks@netcom
# Source directory /u9/resnicks/foo
#
# existing files will NOT be overwritten unless -c is specified
#
# This shar contains:
# length  mode       name
# ------ ---------- ------------------------------------------
#   2749 -rw-r--r-- ct.c
#    435 -rw-r--r-- ct.h
#      0 -rw-r--r-- ct.shar
#    733 -rw-r--r-- main.c
#    200 -rw-r--r-- makefile
#    501 -rw-r--r-- readme
#    946 -rw-r--r-- stack.asm
#    581 -rw-r--r-- task1.c
#
# ============= ct.c ==============
if test -f 'ct.c' -a X"$1" != X"-c"; then
	echo 'x - skipping ct.c (File already exists)'
else
echo 'x - extracting ct.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'ct.c' &&
/*
X * This set of routines implements a simple, cooperative multi-tasking
X * scheduler. scheduling is done on a round-robin basis, running each task
X * one after another for each runnable task. Each running task must decide
X * when to give up it's "slice" to enable other tasks to run.
*/
#include <stdio.h>
#include <setjmp.h>
#include <time.h>
#include <mem.h>
#include <dos.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "ct.h"
X
#define DEAD	0
#define RUN	1
#define START	2
#define BLOCK	3
#define NUMTSKS	10
X
typedef struct _tss
{
X	void (*EntryPt)(void);	/* Task Entry point */
X	int Valid;
X	int NewStack;
X	char *Stack;
X	char Save[sizeof(jmp_buf)];
} Tss;
Tss Tasks[NUMTSKS+2];
X
jmp_buf tsstmp;
jmp_buf _init_senv;
unsigned _stklen = 10240;
int	CurPID = -1;
X
Switcher()
{
static	int OldTI, NewTI =-1, i, j;
static	jmp_buf tmp;
X
X	for (j = i = 0; i < NUMTSKS; i++)
X	{
X		if (Tasks[i].Stack != NULL && Tasks[i].Valid == DEAD)
X		{
X			free(Tasks[i].Stack);
X			Tasks[i].Stack = NULL;
X		}
X		if (Tasks[i].Valid != DEAD)
X			j ++ ;
X	}
X	if (j == 0)
X	{
X		printf("panic: No tasks to run.\n");
X		ResolveSystem();
X	}		
X	OldTI = CurPID;
X	while(Tasks[++CurPID].Valid == DEAD || Tasks[CurPID].Valid == BLOCK )
X		if (CurPID >=  NUMTSKS)
X			CurPID = -1;
X
X	if (CurPID >= NUMTSKS)
X		CurPID = 0;
X	
X	NewTI = CurPID;
X
X	memcpy(Tasks[OldTI].Save,tsstmp,sizeof(jmp_buf));
X	memcpy(tmp,Tasks[NewTI].Save,sizeof(jmp_buf));
X	if (Tasks[NewTI].Valid == START)
X	{
X		Tasks[NewTI].Valid = RUN;
X		StartTask(Tasks[NewTI].EntryPt, Tasks[NewTI].Stack);
X	}
X	longjmp(tmp,1);
}
int CreateTask(void(*EntryPt)(void), int StackSize)
{
X	int i, Ctask = -1;
X	for (i = 0; i < NUMTSKS; i++)
X		if (Tasks[i].Valid == DEAD)
X		{
X			Ctask = i;
X			break ;
X		}
X	if (Ctask == -1)
X		return -1;
X	Tasks[Ctask].EntryPt = EntryPt;
X	Tasks[Ctask].Valid = START;
X	if ((Tasks[Ctask].Stack = calloc(StackSize,1)) == NULL)
X		return -1;
X	Tasks[Ctask].Stack += StackSize;
X	Ctask ++ ;
X	return 0;
}
void EndTask(void)
{
X	Tasks[CurPID].Valid = 	DEAD;
X	TaskSwitch();	
}
DumpTss()
{
X	int i, Num = 0;
X	char *States[] = {"Dead","Running","Loading","Blocked",};
X
X	for (i = 0; i <NUMTSKS; i++)
X		if (Tasks[i].Valid != DEAD)
X			Num ++ ;
X
X	printf("Tasks currently executing: %2d Process Table Size: %2d  \n",
X			Num,NUMTSKS);
X	printf("TID\t\tProgram\t\tStack\t\t   State\r\n");
X	printf("---\t\t---------\t---------\t   -------\n");
X	for(i = 0; i < NUMTSKS; i++)
X	{
X		if (Tasks[i].Valid != DEAD)
X		{
X			printf("%4.4X\t\t",i);
X			printf("%p\t",Tasks[i].EntryPt);
X			printf("%p\t",Tasks[i].Stack);
X			printf("   %-10.10s\n\r",States[Tasks[i].Valid]);
X		}
X	}
X	printf("%70.70s\r","");
}
SHAR_EOF
chmod 0644 ct.c ||
echo 'restore of ct.c failed'
Wc_c="`wc -c < 'ct.c'`"
test 2749 -eq "$Wc_c" ||
	echo 'ct.c: original size 2749, current size' "$Wc_c"
fi
# ============= ct.h ==============
if test -f 'ct.h' -a X"$1" != X"-c"; then
	echo 'x - skipping ct.h (File already exists)'
else
echo 'x - extracting ct.h (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'ct.h' &&
X
extern jmp_buf tsstmp;
extern jmp_buf _init_senv;
X
#define TaskSwitch() {if (setjmp(tsstmp) == 0) Switcher();}
#define DosExitProgram(Rcode) _AX = 0x4C00 | (Rcode&0xFF); geninterrupt(0x21)
#define EnableTasker()	if (setjmp(_init_senv) == 0) Switcher()
#define ResolveSystem() (longjmp(_init_senv,1))
X
int CreateTask(void(*EntryPt)(void), int);
void StartTask(void far (*EntryPt)(void),char far *);
void EndTask(void);
X
X
SHAR_EOF
chmod 0644 ct.h ||
echo 'restore of ct.h failed'
Wc_c="`wc -c < 'ct.h'`"
test 435 -eq "$Wc_c" ||
	echo 'ct.h: original size 435, current size' "$Wc_c"
fi
# ============= ct.shar ==============
if test -f 'ct.shar' -a X"$1" != X"-c"; then
	echo 'x - skipping ct.shar (File already exists)'
else
echo 'x - extracting ct.shar (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'ct.shar' &&
SHAR_EOF
chmod 0644 ct.shar ||
echo 'restore of ct.shar failed'
Wc_c="`wc -c < 'ct.shar'`"
test 0 -eq "$Wc_c" ||
	echo 'ct.shar: original size 0, current size' "$Wc_c"
fi
# ============= main.c ==============
if test -f 'main.c' -a X"$1" != X"-c"; then
	echo 'x - skipping main.c (File already exists)'
else
echo 'x - extracting main.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'main.c' &&
#include <stdio.h>
#include <setjmp.h>
#include <time.h>
#include <mem.h>
#include <dos.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "ct.h"
X
void Task1(void);
void Task2(void);
X
X
X
main()
{
X 	CreateTask(Task1,4096);
X 	CreateTask(Task2,4096);
X 	CreateTask(Task2,4096);
X 	CreateTask(Task2,4096);
X 	clrscr();
X 	EnableTasker();
X	printf("\n\nProgram terminated normally.\n");
X	return 0;
}
void Task1(void)
{
X	int PC = 0;
X	while(PC < 0x80)
X	{
X		printf("\033[9;1H\033[7mTask 1 PC=%4.4X\033[0m",PC);
X		printf("\033[10;20HProcess Status Task\r\n");
X		DumpTss();
X		printf("\r\n\n");
X		PC ++ ;
X		if (kbhit())
X			if (getch() == 27)
X				ResolveSystem();
X		TaskSwitch();
X	}
}
X
X
SHAR_EOF
chmod 0644 main.c ||
echo 'restore of main.c failed'
Wc_c="`wc -c < 'main.c'`"
test 733 -eq "$Wc_c" ||
	echo 'main.c: original size 733, current size' "$Wc_c"
fi
# ============= makefile ==============
if test -f 'makefile' -a X"$1" != X"-c"; then
	echo 'x - skipping makefile (File already exists)'
else
echo 'x - extracting makefile (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'makefile' &&
OBJS=main.obj ct.obj task1.obj stack.obj
CARGS=-ml -v -M -a -d -f- -r -N- -Z
X
ctsk.exe: $(OBJS)
X	cc -eCTSK.EXE $(CARGS) $(OBJS)
X
.c.obj:
X	cc $(CARGS) -c $<
X
.asm.obj:
X	tasm /Zi /Mx stack
X
SHAR_EOF
chmod 0644 makefile ||
echo 'restore of makefile failed'
Wc_c="`wc -c < 'makefile'`"
test 200 -eq "$Wc_c" ||
	echo 'makefile: original size 200, current size' "$Wc_c"
fi
# ============= readme ==============
if test -f 'readme' -a X"$1" != X"-c"; then
	echo 'x - skipping readme (File already exists)'
else
echo 'x - extracting readme (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'readme' &&
This is my multi-threaded C "library". This is public domain, you may
do with it as you will. I wrote this as an experiment, and it seems to
work, but I haven't done anything of great signifigance. This is for
large model Turbo/Borland C. It has also been tested in Protected Mode
under OS/2 using Microsoft C 6.0, but there is little practical use there :)
X
Have fun!
X
X
Steve Resnick
resnicks@netcom.com
steve@camphq
steve.resnick@f105.n143.z1.FIDONET.ORG
(408) 263-8017 12/2400 IFNA node 143/105.0
X
SHAR_EOF
chmod 0644 readme ||
echo 'restore of readme failed'
Wc_c="`wc -c < 'readme'`"
test 501 -eq "$Wc_c" ||
	echo 'readme: original size 501, current size' "$Wc_c"
fi
# ============= stack.asm ==============
if test -f 'stack.asm' -a X"$1" != X"-c"; then
	echo 'x - skipping stack.asm (File already exists)'
else
echo 'x - extracting stack.asm (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'stack.asm' &&
.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
X	PUSH	BP
X	MOV	BP,SP			; Save current stack frame
X	MOV	AX,[BP+12]		; Segment Address of New Stack
X	MOV	BX,[BP+10]		; Offset Address of New Stack
X	MOV	CX,[BP+8]		; Segment Address of entry point
X	MOV	DX,[BP+6]		; Offset Address of entry point
X	MOV	SS,AX			; Select new stack segment 
X	MOV	SP,BX			; And set new stack pointer
X	MOV	AX,offset CEtsk		; Get IP Value for dummy return
X	PUSH	CS			; Make return segment point to here
X	PUSH	AX			; Make return offset  point to CEtsk
X	PUSH	CX			; Push return CS
X	PUSH	DX			; Push Return IP
X	RET
CEtsk:	CALL	far ptr _EndTask
_StartTask	ENDP
X	END
X	
X
X 	
SHAR_EOF
chmod 0644 stack.asm ||
echo 'restore of stack.asm failed'
Wc_c="`wc -c < 'stack.asm'`"
test 946 -eq "$Wc_c" ||
	echo 'stack.asm: original size 946, current size' "$Wc_c"
fi
# ============= task1.c ==============
if test -f 'task1.c' -a X"$1" != X"-c"; then
	echo 'x - skipping task1.c (File already exists)'
else
echo 'x - extracting task1.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'task1.c' &&
#include <stdio.h>
#include <setjmp.h>
#include <time.h>
#include <mem.h>
#include <dos.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "ct.h"
X
extern int CurPID;
X
void Task2(void)
{
X	int Row, Num, EndPoint, PC = 0;
X	int EndPoints[] = {0,0x20,0x40,0x60};
X	Num = CurPID;
X	Row = (Num * 2) + 1;
X	EndPoint = EndPoints[CurPID];
X
X	Num ++ ;
X	while(PC < EndPoint + 1)
X	{
X		printf("\033[%d;1H\033[7mTask %d PC=%4.4X\033[0m",Row,Num,PC);
X		PC ++ ;
X		TaskSwitch();
X	}
X	printf("\033[%d;1HTask %d: Terminating.\n",Row+1,Num,PC);
X	return ;
}
SHAR_EOF
chmod 0644 task1.c ||
echo 'restore of task1.c failed'
Wc_c="`wc -c < 'task1.c'`"
test 581 -eq "$Wc_c" ||
	echo 'task1.c: original size 581, current size' "$Wc_c"
fi
exit 0



-- 
-------------------------------------------------------------------------------
	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
-------------------------------------------------------------------------------

tabu6@CCVAX.IASTATE.EDU (Adam Goldberg) (04/03/91)

In article <1991Apr2.212636.14024@netcom.COM>, resnicks@netcom.COM (Steve Resnick) writes:
>
>There seems to have been a bit of discussion about multi-threaded C.
>Having written some code for this as an experiment, I find I have no 
>real need for it :) (But give me a preemptive scheduler and I am happy =])
>
>If anyone is interested, here it is. This seems to work both with Turbo C,
>and MSC 6.0 (under OS/2). The OS/2 part seems utterly useless, but what
>the heck ....
>Have fun :) 
>

The April issue of Dr. Dobb's has an article regarding run-until-block
multitasking using C, including source code and an explaination thereof.

+----------------------------------------------------------------------------+
+ Adam Goldberg                         Bitnet:   tabu6@ISUVAX.BITNET        +
+ Iowa State University                 Internet: tabu6@CCVAX.IASTATE.EDU    +
+          "It's simple!  Even a Pascal programmer could do it!"             +
+ "Remember: The sooner you fall behind, the more time you have to catch up" +
+----------------------------------------------------------------------------+

resnicks@netcom.COM (Steve Resnick) (04/04/91)

In article <1991Apr3.011816.15419@news.iastate.edu> tabu6@CCVAX.IASTATE.EDU writes:
>In article <1991Apr2.212636.14024@netcom.COM>, resnicks@netcom.COM (Steve Resnick) writes:
>>
>>There seems to have been a bit of discussion about multi-threaded C.
>>Having written some code for this as an experiment, I find I have no 
>>real need for it :) (But give me a preemptive scheduler and I am happy =])
>>
>>If anyone is interested, here it is. This seems to work both with Turbo C,
>>and MSC 6.0 (under OS/2). The OS/2 part seems utterly useless, but what
>>the heck ....
>>Have fun :) 
>>
>
>The April issue of Dr. Dobb's has an article regarding run-until-block
>multitasking using C, including source code and an explaination thereof.


I never saw the Dr. Dobbs version, but the one in the C User's Journal
doesn't work. (Not under TC 2.0, BC 2.0, or MSC 6.0)

Soap Box: On

Personally, I have been boycotting Dr. Dobbs because of the way they
offer a "no risk" subscription. Get your first issue, and mark "CANCEL"
on the bill. Instead of cancelling my subscription, their distribution/
subscription service sent me threatening letters telling me how my credit
is going to be damaged. Since this is a service and not the publisher, I
am left with no way to contact these people, except through a PO box
in Colorado. That's no way to do business, and I for one will not support
any business which resorts to such tactics.

Soap Box: Off

-- 
-------------------------------------------------------------------------------
	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
-------------------------------------------------------------------------------

jwm712@unhd.unh.edu (Jonathan W Miner) (04/04/91)

In article <1991Apr3.011816.15419@news.iastate.edu> tabu6@CCVAX.IASTATE.EDU writes:
>In article <1991Apr2.212636.14024@netcom.COM>, resnicks@netcom.COM (Steve Resnick) writes:
>>
>>There seems to have been a bit of discussion about multi-threaded C.
>>Having written some code for this as an experiment, I find I have no 
>>real need for it :) (But give me a preemptive scheduler and I am happy =])
>>
>>If anyone is interested, here it is. This seems to work both with Turbo C,
>>and MSC 6.0 (under OS/2). The OS/2 part seems utterly useless, but what
>>the heck ....
>>Have fun :) 
>>
>
>The April issue of Dr. Dobb's has an article regarding run-until-block
>multitasking using C, including source code and an explaination thereof.
>

I have downloaded CTASK22 from wuarchive and that works well.  I compiled
under Turbo C with no problems.  Just my $0.02


-- 
Jonathan Miner        | I don't speak for UNH, and UNH does not 
jwm712@unhd.unh.edu   | speak for me! 
(603)868-3416         | Hacking to survive......

chetl@sparc23.tamu.EDU (Chet T Laughlin) (04/04/91)

Xcuse me, but why are you writing your own task handler under OS/2?  Seems
redundant to me when you already have OS support for processes and threads.
Its not like you HAVE to write one, as one would have to under DOS.
-- 
+-----------------------------------------------------+
|    Chet Laughlin        chetl@sparc21.tamu.edu      |
|  I have no opinions, my lawyers told me so.  My     |
|  existance is currently under debate.               |
+-----------------------------------------------------+