/*
* 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 * ) & lthreads [ 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 ;
}