/* * Horrid example of an in-process cooperative multi-tasking scheduler * using GNU C label addresses, local stacks and a process table. Also * using select() for possible extensions with timers, events and * whatnot. * * Possible enchancements include making initial checks and the goto * for each 'program' into some form of macro and adding some form of * registration service in the scheduler to wait for I/O and * stuff. Left as an exercise to the interested reader. * * M.C. Widerkrantz, mc at hack.org, http://hack.org/mc/ * 2004-03-19 (2) */ #include #include #include #include /* Values plucked out of the blue. Change at will. */ #define STACKDEPTH 1024 #define MAXPROC 17 /* Some shorthand macros. Way too bothersome to type. */ #define push(x) proc[pid].stack[proc[pid].top ++] = x #define pop() *proc[pid].stack[(proc[pid].top --) - 1] #define top() proc[pid].top /* A function pointer type. */ typedef void (*funp_t)(); /********** Globals **********/ #define NOPROC -1 int maxpid = NOPROC; /* Process table. */ struct process { funp_t funp; int top; void *stack[STACKDEPTH]; } proc[MAXPROC]; /********** Helper functions **********/ int newprocess(funp_t funp) { if (maxpid < MAXPROC) { maxpid ++; proc[maxpid].funp = funp; proc[maxpid].top = 0; } else { /* Obviously, a little more graceful form of recovery would * be nice... */ printf("Process table full. Exiting...\n"); exit(1); } return maxpid; } /**********The 'programs' **********/ void A() { static int pid = NOPROC; /* Does this function have a running process? */ if (NOPROC != pid) { /* Check for stack underflow. */ if (0 == top()) { return; } /* * This is the 'interesting' goto. Pops stack for label * address. */ goto pop(); } else { /* Start new process. */ pid = newprocess(A); } start: puts("A start"); push(&&inbetween); return; inbetween: puts("A inbetween"); push(&&later); return; later: puts("A later"); return; } /* * Another program. Could be entirely different, but I'm too lazy to change it much. */ void B() { static int pid = NOPROC; /* Does this function have a running process? */ if (NOPROC != pid) { /* Check for stack underflow. */ if (0 == top()) { return; } /* Pop stack for label address and goto it. */ goto pop(); } else { /* Start new process. */ pid = newprocess(B); } start: puts("B start"); push(&&inbetween); return; inbetween: puts("B inbetween"); push(&&later); return; later: puts("B later"); return; } int main(void) { struct timeval tv; int found, i; /* * A NULL funp means the process is unallocated, so initalize all * processes to be unallocated. */ for (i = 0; i <= MAXPROC; i ++) { proc[i].funp = NULL; } /* Call some functions, i.e. start some processes. */ A(); B(); /* The Scheduler, Idle Loop, Whatever */ while (1) { /* * Fall through once each second. Could be expanded to hilariously * complex things. */ tv.tv_sec = 1L; tv.tv_usec = 0L; found = select(1, NULL, NULL, NULL, &tv); if (0 < found) { perror("select"); } /* * Go through process table, check for waiting I/O, give them * some share of the CPU, that sort of stuff. For now, just * call the function. */ for (i = 0; i <= MAXPROC; i ++) { if (NULL != proc[i].funp) { printf(" serving process %d\n", i); proc[i].funp(); } } } exit(0); }