/*********** THREAD.C COPYRIGHT 1989 GREGORY COLVIN ************
This program may be distributed free with this copyright notice.
***************************************************************/
#include "thread.h"
thread *Threads; /* table of threads */
int ThCurr=1; /* current executing thread */
static int N_Threads; /* number of threads in table */
static int Free=2; /* first free thread */
static int Next=1; /* next runnable thread */
static char *Stack; /* bottom of stack for init */
static void (*Root)(void); /* for temporary use in exec */
thread *ThInit(int n,int size) /* create n size byte threads */
{ int i;
if (!N_Threads) { /* if just entered */
if (n < 2)
return 0; /* error, n too small */
Threads= /* create table */
(thread *)calloc(n,sizeof(thread));
if (!Threads)
return Threads; /* error, bad calloc */
Threads--; /* will index from 1 */
N_Threads= n, n= 1;
if (setjmp(Threads[1].exit)) /* set exit point */
exit(0); /* exit init thread */
} else if (!Stack) { /* start new thread */
Stack= (char *)&size; /* at top of stack */
if (setjmp(Threads[n].exec)) { /* set entry point */
if (!setjmp(Threads[ThCurr].exit))/* set exit point */
(*Root)(); /* call root function */
Threads[ThCurr].free= Free; /* come here on exit */
Free= ThCurr; /* put on free list */
Next= Threads[Free].next; /* take off run list */
for (i=1; i <= N_Threads; i++) { /* clean up table */
if (Threads[i].parent == Free) /* if abandoned child */
Threads[i].parent = 1; /* adopt by init */
if (Threads[i].next == Free) /* patch run list */
Threads[i].next= Threads[Free].next;
}
ThCurr= Threads[Free].parent; /* will jump to parent */
Threads[Free].parent= 0; /* Free is parentless */
longjmp(Threads[ThCurr].jump,Free);
}
}
if (Stack - (char *)&size < size) /* if not enough stack */
ThInit(n,size); /* push more stack */
else { /* done with a thread */
Threads[n].stack =(char *)&size; /* save top of stack */
Stack= 0 ; /* start new thread */
if (n < N_Threads) { /* if not done */
Threads[n].free= n + 1; /* link to free list */
ThInit(++n,size); /* push more stack */
} else
Threads[n].free = 0; /* at end of free list */
}
return Threads; /* done: return table */
}
void ThFree() /* free the thread table */
{ free (Threads+1); /* goodbye */
Threads= 0, N_Threads= 0; /* can init again */
}
int ThNew(void (*root)(void)) /* fork and exec new thread */
{ int parent, fork;
ThProbe(); /* stack probe */
fork= Free; /* fork to free thread*/
if (fork == 0)
return -1; /* error, none free */
Free= Threads[fork].free; /* take off free list */
parent= ThCurr; /* current is parent */
Threads[fork].parent= parent; /* set parent */
ThCurr= fork; /* will run fork next */
if (!Threads[Next].next) /* link to run list */
Threads[ThCurr].next= Next; /* make circular list */
else
Threads[ThCurr].next= Threads[Next].next;
Threads[Next].next= ThCurr;
Next= ThCurr; /* next on run list */
if (setjmp(Threads[parent].jump)) /* put parent to sleep */
return fork; /* parent is awake */
Root= root; /* who to call */
longjmp(Threads[ThCurr].exec,fork); /* call root from init */
}
void ThExit(void) /* exit to parent */
{ ThProbe(); /* stack probe */
longjmp(Threads[ThCurr].exit,ThCurr);
}
int ThJump(int id) /* jump to another thread */
{ int jumper, caller;
ThProbe(); /* stack probe */
if (id == 0 ) /* if no destination */
id= Threads[ThCurr].next; /* next on run list */
if (id < 1 || id > N_Threads || Threads[id].parent < 0)
return -1; /* error, bad id */
caller= ThCurr; /* where we came from */
if (id == caller)
return ThCurr; /* nowhere to go */
ThCurr= id; /* where we are going */
if (jumper=setjmp(Threads[caller].jump))
return jumper; /* return who jumped */
longjmp(Threads[id].jump,caller); /* jump to ThCurr */
}
static void test()
{
ThProbe();
printf("test: called from thread %d\n",ThId());
ThJump(0);
printf("test: falling off thread %d\n",ThId());
}
main()
{ int i;
ThInit(3,2048);
for (i=1; i <= 9; i++) {
printf("main: loop %d\n",i);
printf("main: created new thread %d\n",ThNew(test));
printf("main: created new thread %d\n",ThNew(test));
printf("main: exited from thread %d\n",ThJump(0));
printf("main: exited from thread %d\n",ThJump(0));
}
}