Till now, our scheduler has envoked each process every time through its loop. As the context that has to be saved and restored for each process gets bigger, this 'context switching' gets to be more and more expensive of CPU cycles, and we'd like to avoid it by not running any process that doesn't have anything it needs to do. There are a couple of ways to achieve this. 1) Test based invocation. Something I first saw in DEC's tops20 operating system was a scheme where each process could supply to the scheduler (part of its context) a function that would test whether the process was runnable or not. This may sound the same as running the process to figure it out, but in this case the TEST is done in the scheduler context, so it is cheaper than actually doing the context switches necesary to run the process. So we add to our process context: typedef struct context_t { mempointer schedsp; mempointer sp; mempointer stack; int stacksize; int proc_id; function_ptr test_func; /* A function to test runability */ mempointer test_func_argument; /* A pointer to an argument for test */ }; /* And a new function: */ void suspend_for_test (function_ptr myfunction, mempointer argument) { context.test_func = myfunction; /* Set test in our context */ context.test_func_argument = argument; /* and its arguments */ suspend(); /* and go to scheduler context */ } Inside the scheduler, we have something like: while (TRUE) { FOR_ALL_PROCESSES(pid) { pcontext = processarray[pid]; /* * execute test function in scheduler context */ if (FUNCTION_EXECUTE(pcontext.myfunction(pcontext.argument))) { /* * if the test succeeds, set up the current process to be * run, and switch our context to it to run the process */ context = pcontext; resume(); } } } and our process might have: process translate() { char c; while (TRUE) { c = getcharacter(); putcharacter(translate(c)); } } /* * scheduler test to check whether a uart bit is true. */ boolean uart_test (int bit) { if (uart.status & bit) return(TRUE); /* If the bit is set, we can awake */ return(FALSE) /* Otherwise we should stay suspended */ } char getcharacter() { suspend_for_test(uart_test, RXRDY); /* Wait for rx character */ return(uart->data); } putcharacter (char x) { suspend_for_test(uart_test, TXRDY); /* wait for tx space */ uart->data = x; } Not a lot more than this is sufficient to write quite complex pieces of embedded software, although not necessarilly "real time" embedded software. Since the scheduler has no control over how long each process runs before it suspends, in can't guarantee a response time to any particular change in status within any particular time. Still, a little discipline on the part of the programmer(s) can take the place of a lot of scheduler/tasking complexity, at least till your project gets too big to control :-) In particular, by allowing the individual processes to decide when to suspend(), you can pretty much do away with the need for any form of data structure locking - instead, you just avoid suspending while your (shared) data structures are inconsistant. BillW