🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.os.msdos.programmer
Article #5576

Multi-Threaded C

#5576
From: Steve Resnick
Date: Tue, 02 Apr 1991 21:26
422 lines
11755 bytes
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
-------------------------------------------------------------------------------
Technical Details
Message-ID: <1991Apr2.212636.14024@netcom.COM>
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!swrinde!zaphod.mps.ohio-state.edu!pacific.mps.ohio-state.edu!linac!att!pacbell.com!tandem!netcom!resnicks

Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!utzoo!utgpu!news-server.csri.toronto.edu!cs.utexas.edu!swrinde!zaphod.mps.ohio-state.edu!pacific.mps.ohio-state.edu!linac!att!pacbell.com!tandem!netcom!resnicks
From: resnicks@netcom.COM (Steve Resnick)
Newsgroups: comp.os.msdos.programmer
Subject: Multi-Threaded C
Keywords: Turbo C, Multi-threaded C, MS DOS, kludge
Message-ID: <1991Apr2.212636.14024@netcom.COM>
Date: 2 Apr 91 21:26:36 GMT
Sender: resnicks@netcom.COM (Steve Resnick)
Organization: Me? Organized? HA!
Lines: 422