/* LWThreads.c, Copyright (c) by George Fankhauser, Swiss Federal Institute of Technology, Computer Engineering and Networks Laboratory. TOPSY -- A Teachable Operating System. Implementation of a tiny and simple micro kernel for teaching purposes. For further information, please visit http://www.tik.ee.ethz.ch/~topsy This software is provided under the terms of the GNU General Public Licence. A full copy of the GNU GPL is provided in the file COPYING found in the development root of Topsy. */ /* Revision 1.2 1999/10/25 18:24:51 gfa *** empty log message *** */ /* Topsy LightWeight Threads This package offers (more or less) the same as Topsy messages and threads do. The main diffs are: - they do not work across address spaces (user/kernel in Topsy) - they are not preemptable - they are much faster - quite small (about 1500 bytes machine code) - messages are not queued - allocation of structures is static, i.e. one has to know in advance how many threads will be used - uses only vmAlloc/vmFree (could be replaced with malloc/free on unix) - uses only a minimum HAL: save/restore/setarg (similar to setjmp/longjmp) */ #include "LWThreads.h" // ptr to main struct static LWThreadArrayDesc* lw; // implemented in asm extern lwSave(void*, void*, void*); extern lwRestore(long, long, long); extern lwSetArg(ThreadArg parameter); static void lwSwitchTo(ThreadId to) { LWThread from = &lw->a[lw->current]; // save // we don't want to trash ra... #ifdef mips asm("move $7,$31"); // move a3, ra #endif lwSave(&from->context.stackPointer, &from->context.framePointer, &from->context.programCounter); // restore lw->current = to; lwRestore(from->context.stackPointer, from->context.framePointer, from->context.programCounter); // lwRestore does not return } static void lwSwitchToParam(ThreadId id, ThreadArg parameter) { lwSetArg(parameter); lwSwitchTo(id); } LWError lwInit(unsigned int noOfThreads) { int size = sizeof(LWThreadArrayDesc) + noOfThreads*sizeof(LWThreadDesc); if (vmAlloc(size, &lw) == VM_ALLOCOK) { lw->N = noOfThreads; lw->n = 0; lw->current = LW_NO_THREAD; { int i; for (i = 0; i < lw->N; i++) { lw->a[i].next = LW_NO_THREAD; lw->a[i].prev = LW_NO_THREAD; lw->a[i].state = LW_THREAD_DOES_NOT_EXIST; lw->a[i].msg = NULL; } } } else { return LW_FAILED; } } LWError lwExit() { int i; for (i = 0; i < lw->N; i++) { lwKill(i); } if (vmFree(lw) == VM_FREEOK) { return LW_OK; } else { return LW_FAILED; } } LWError lwMsgSend(ThreadId to, Message *msg) { LWThread receiver = &lw->a[to]; // redirect self if (to == LW_SELF) to = lw->current; // check existence if (to >= lw->N || to < 0) { return LW_FAILED; } if (receiver->state == LW_THREAD_DOES_NOT_EXIST) { return LW_FAILED; } /* regardless of state, pass message on */ receiver->msg = msg; receiver->from = lw->current; receiver->state = LW_STATE_READY; lwSwitchTo(to); } /* timeout and msgId is for signature compatibility only and ignored */ LWError lwMsgRecv(ThreadId* from, MessageId msgId, Message* msg, int timeOut) { LWThread current = &lw->a[lw->current]; if (current->msg == NULL) { current->state = LW_STATE_WAIT; lwYield(); } // deliv msg msg = current->msg; *from = current->from; current->msg = NULL; current->from = LW_NO_THREAD; // cont exec return; } /* name is for signature compatibility only and silently ignored */ LWError lwStart(ThreadId* id, ThreadMainFunction mainFunction, ThreadArg parameter, char *name) { int i; ThreadId t1, t2; if (lw->n >= lw->N) return LW_FAILED; for (i = 0; i < lw->N; i++) { if (lw->a[i].state != LW_THREAD_DOES_NOT_EXIST) { i++; } } // set info lw->n++; *id = i; lw->a[i].state = LW_STATE_READY; lw->a[i].msg = NULL; { LWThreadContext* ctxt = &(lw->a[i].context); ctxt->stackPointer=(long)(ctxt->stack) + LW_STACKSIZE - 4; ctxt->framePointer = 0; ctxt->programCounter = (unsigned long)mainFunction; } if (lw->current == LW_NO_THREAD) { lw->current = i; lw->a[i].next = i; lw->a[i].prev = i; } else { t1 = lw->a[lw->current].next; t2 = lw->current; lw->a[i].next = t2; lw->a[i].prev = t1; lw->a[t1].next = i; lw->a[t2].prev = i; lw->current = i; } lwSwitchToParam(i, parameter); return LW_OK; } LWError lwKill(ThreadId id) { ThreadId t1, t2; if (lw->n <= 0) return LW_FAILED; if (id >= lw->N || id < 0) return LW_FAILED; if (lw->a[id].state == LW_THREAD_DOES_NOT_EXIST) return LW_FAILED; lw->a[id].state = LW_THREAD_DOES_NOT_EXIST; if (lw->n == 1) { lw->current = LW_NO_THREAD; } else { t2 = lw->a[lw->current].next; t1 = lw->a[lw->current].prev; lw->a[t1].next = t2; lw->a[t2].prev = t1; } lw->n--; return LW_OK; } void lwYield() { ThreadId next = lw->a[lw->current].next; while (lw->a[next].state != LW_STATE_READY) { next = lw->a[next].next; // if (next == lw->current) !deadlock! ; } lwSwitchTo(next); }