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