/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include <common.h> #include <exports.h> /* * Author: Arun Dharankar <ADharankar@ATTBI.Com> * * A very simple thread/schedular model: * - only one master thread, and no parent child relation maintained * - parent thread cannot be stopped or deleted * - no permissions or credentials * - no elaborate safety checks * - cooperative multi threading * - Simple round-robin scheduleing with no priorities * - no metering/statistics collection * * Basic idea of implementing this is to allow more than one tests to * execute "simultaneously". * * This may be modified such thread_yield may be called in syscalls, and * timer interrupts. */ #define MAX_THREADS 8 #define CTX_SIZE 512 #define STK_SIZE 8*1024 #define STATE_EMPTY 0 #define STATE_RUNNABLE 1 #define STATE_STOPPED 2 #define STATE_TERMINATED 2 #define MASTER_THREAD 0 #define RC_FAILURE (-1) #define RC_SUCCESS (0) typedef vu_char *jmp_ctx; unsigned long setctxsp (vu_char *sp); int ppc_setjmp(jmp_ctx env); void ppc_longjmp(jmp_ctx env, int val); #define setjmp ppc_setjmp #define longjmp ppc_longjmp struct lthread { int state; int retval; char stack[STK_SIZE]; uchar context[CTX_SIZE]; int (*func) (void *); void *arg; }; static volatile struct lthread lthreads[MAX_THREADS]; static volatile int current_tid = MASTER_THREAD; static uchar dbg = 0; #define PDEBUG(fmt, args...) { \ if(dbg != 0) { \ printf("[%s %d %s]: ",__FILE__,__LINE__,__FUNCTION__);\ printf(fmt, ##args); \ printf("\n"); \ } \ } static int testthread (void *); static void sched_init (void); static int thread_create (int (*func) (void *), void *arg); static int thread_start (int id); static void thread_yield (void); static int thread_delete (int id); static int thread_join (int *ret); #if 0 /* not used yet */ static int thread_stop (int id); #endif /* not used yet */ /* An example of schedular test */ #define NUMTHREADS 7 int sched (int ac, char *av[]) { int i, j; int tid[NUMTHREADS]; int names[NUMTHREADS]; app_startup(av); sched_init (); for (i = 0; i < NUMTHREADS; i++) { names[i] = i; j = thread_create (testthread, (void *) &names[i]); if (j == RC_FAILURE) printf ("schedtest: Failed to create thread %d\n", i); if (j > 0) { printf ("schedtest: Created thread with id %d, name %d\n", j, i); tid[i] = j; } } printf ("schedtest: Threads created\n"); printf ("sched_test: function=0x%08x\n", (unsigned)testthread); for (i = 0; i < NUMTHREADS; i++) { printf ("schedtest: Setting thread %d runnable\n", tid[i]); thread_start (tid[i]); thread_yield (); } printf ("schedtest: Started %d threads\n", NUMTHREADS); while (1) { printf ("schedtest: Waiting for threads to complete\n"); if (tstc () && getc () == 0x3) { printf ("schedtest: Aborting threads...\n"); for (i = 0; i < NUMTHREADS; i++) { printf ("schedtest: Deleting thread %d\n", tid[i]); thread_delete (tid[i]); } return RC_SUCCESS; } j = -1; i = thread_join (&j); if (i == RC_FAILURE) { printf ("schedtest: No threads pending, " "exiting schedular test\n"); return RC_SUCCESS; } printf ("schedtest: thread is %d returned %d\n", i, j); thread_yield (); } return RC_SUCCESS; } static int testthread (void *name) { int i; printf ("testthread: Begin executing thread, myname %d, &i=0x%08x\n", *(int *) name, (unsigned)&i); printf ("Thread %02d, i=%d\n", *(int *) name, i); for (i = 0; i < 0xffff * (*(int *) name + 1); i++) { if (tstc () && getc () == 0x3) { printf ("testthread: myname %d terminating.\n", *(int *) name); return *(int *) name + 1; } if (i % 100 == 0) thread_yield (); } printf ("testthread: returning %d, i=0x%x\n", *(int *) name + 1, i); return *(int *) name + 1; } static void sched_init (void) { int i; for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) lthreads[i].state = STATE_EMPTY; current_tid = MASTER_THREAD; lthreads[current_tid].state = STATE_RUNNABLE; PDEBUG ("sched_init: master context = 0x%08x", (unsigned)lthreads[current_tid].context); return; } static void thread_yield (void) { static int i; PDEBUG ("thread_yield: current tid=%d", current_tid); #define SWITCH(new) \ if(lthreads[new].state == STATE_RUNNABLE) { \ PDEBUG("thread_yield: %d match, ctx=0x%08x", \ new, \ (unsigned)lthreads[current_tid].context); \ if(setjmp(lthreads[current_tid].context) == 0) { \ current_tid = new; \ PDEBUG("thread_yield: tid %d returns 0", \ new); \ longjmp(lthreads[new].context, 1); \ } else { \ PDEBUG("thread_yield: tid %d returns 1", \ new); \ return; \ } \ } for (i = current_tid + 1; i < MAX_THREADS; i++) { SWITCH (i); } if (current_tid != 0) { for (i = 0; i <= current_tid; i++) { SWITCH (i); } } PDEBUG ("thread_yield: returning from thread_yield"); return; } static int thread_create (int (*func) (void *), void *arg) { int i; for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) { if (lthreads[i].state == STATE_EMPTY) { lthreads[i].state = STATE_STOPPED; lthreads[i].func = func; lthreads[i].arg = arg; PDEBUG ("thread_create: returns new tid %d", i); return i; } } PDEBUG ("thread_create: returns failure"); return RC_FAILURE; } static int thread_delete (int id) { if (id <= MASTER_THREAD || id > MAX_THREADS) return RC_FAILURE; if (current_tid == id) return RC_FAILURE; lthreads[id].state = STATE_EMPTY; return RC_SUCCESS; } static void thread_launcher (void) { PDEBUG ("thread_launcher: invoking func=0x%08x", (unsigned)lthreads[current_tid].func); lthreads[current_tid].retval = lthreads[current_tid].func (lthreads[current_tid].arg); PDEBUG ("thread_launcher: tid %d terminated", current_tid); lthreads[current_tid].state = STATE_TERMINATED; thread_yield (); printf ("thread_launcher: should NEVER get here!\n"); return; } static int thread_start (int id) { PDEBUG ("thread_start: id=%d", id); if (id <= MASTER_THREAD || id > MAX_THREADS) { return RC_FAILURE; } if (lthreads[id].state != STATE_STOPPED) return RC_FAILURE; if (setjmp (lthreads[current_tid].context) == 0) { lthreads[id].state = STATE_RUNNABLE; current_tid = id; PDEBUG ("thread_start: to be stack=0%08x", (unsigned)lthreads[id].stack); setctxsp ((vu_char *)<hreads[id].stack[STK_SIZE]); thread_launcher (); } PDEBUG ("thread_start: Thread id=%d started, parent returns", id); return RC_SUCCESS; } #if 0 /* not used so far */ static int thread_stop (int id) { if (id <= MASTER_THREAD || id >= MAX_THREADS) return RC_FAILURE; if (current_tid == id) return RC_FAILURE; lthreads[id].state = STATE_STOPPED; return RC_SUCCESS; } #endif /* not used so far */ static int thread_join (int *ret) { int i, j = 0; PDEBUG ("thread_join: *ret = %d", *ret); if (!(*ret == -1 || *ret > MASTER_THREAD || *ret < MAX_THREADS)) { PDEBUG ("thread_join: invalid tid %d", *ret); return RC_FAILURE; } if (*ret == -1) { PDEBUG ("Checking for tid = -1"); while (1) { /* PDEBUG("thread_join: start while-loopn"); */ j = 0; for (i = MASTER_THREAD + 1; i < MAX_THREADS; i++) { if (lthreads[i].state == STATE_TERMINATED) { *ret = lthreads[i].retval; lthreads[i].state = STATE_EMPTY; /* PDEBUG("thread_join: returning retval %d of tid %d", ret, i); */ return RC_SUCCESS; } if (lthreads[i].state != STATE_EMPTY) { PDEBUG ("thread_join: %d used slots tid %d state=%d", j, i, lthreads[i].state); j++; } } if (j == 0) { PDEBUG ("thread_join: all slots empty!"); return RC_FAILURE; } /* PDEBUG("thread_join: yielding"); */ thread_yield (); /* PDEBUG("thread_join: back from yield"); */ } } if (lthreads[*ret].state == STATE_TERMINATED) { i = *ret; *ret = lthreads[*ret].retval; lthreads[*ret].state = STATE_EMPTY; PDEBUG ("thread_join: returing %d for tid %d", *ret, i); return RC_SUCCESS; } PDEBUG ("thread_join: thread %d is not terminated!", *ret); return RC_FAILURE; }