/*     
 * $RCSfile: thread.c,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: thread.c,v $
 * Revision 1.7  1997/03/17 06:39:35  dorgival
 * Changed calls to lock/unlock the x-kernel to fix deadlock problem
 *
 * Revision 1.6  1997/03/17  01:36:11  dorgival
 * Improved tracing with macros around pthread_mutex/cond calls
 * Updated code to print new information in thread_dump
 *
 * Revision 1.5  1997/03/16  18:30:24  dorgival
 * Increased tracing info with this_thread->status
 * Started breaking xk_lock to make mutex locking external (USE_SEM_MUTEX)
 *
 * Revision 1.4  1997/03/12 23:03:34  dorgival
 * merged xk/p routines, like thread_stub and ThreadCreate
 *
 * Revision 1.3  1997/03/11 21:49:27  dorgival
 * Added synchronization at beginning of new threads through thread->mutex
 *
 * Revision 1.2  1997/03/11 20:57:59  dorgival
 * Created xk_thread_t and updated threadSelf()
 *
 * Revision 1.1  1997/03/11 20:47:08  dorgival
 * Initial revision
 *
 *
 */

/*
 * Thread handling for the x-kernel running in user-mode in the Paragon.
 *
 * In this case we are using the OSF-1-provided P-threads package
 * and implementing the basic functions needed by other modules and
 * protocols. There is not provision right now for the special needs
 * of device drivers and other threads external to the x-kernel.
 * They should either use P-threads directly or build their interface.
 */

/*
 * The first version of this module controlled access to the x-kernel
 * with only one mutex variable. Processes trying to enter the kernel
 * just tried to get a lock on the mutex. It turns out the way locking
 * is implemented (by busy waiting+yielding) this is not a good idea.
 * The right way to do it is by making processes to wait on (at least
 * one) condition variable, when they are suspended for good.
 * On controlling the access it may be a good idea to use different
 * condition variables for different types of threads: external,
 * threads suspended in the x-kernel, and shepherd threads are
 * three cathegories so far. Then we can assign different priorities
 * for lock the kernel.
 */

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/timers.h>

#include "/usr/include/pthread.h"

#include "event.h"
#include "event_i.h"
#include "xk_thread.h"
#include "trace.h"
#include "xk_debug.h"
#include "x_util.h"

#define SUGG_MAX_THREAD    6 /* The manuals suggest never to have more than 6 */
#define DEFAULT_STACK_SIZE      (16*INIT_STACK_SIZE)

#define PTHREAD_DETACH( pthread ) \
do {                              \
    pthread_t temp = (pthread);   \
    pthread_detach(&(pthread));   \
    (pthread) = temp;             \
} while (0)

int tracethreads;

int    xk_thread_count, p_thread_count;
static pthread_attr_t  xk_thread_attr;

/*
 * xk_lock is used to guarantee access to the x-kernel is done on
 * a mutual exclusion basis. There are three levels of access: Every
 * time control of the x-kernel is relinguished and may be assigned
 * to another thread, first an internal queue of locked threads is
 * verified and if a thread is found in it, it gets the kernel. Otherwise
 * any thread in a lock for shepherd threads is waked, and finally
 * external threads which require access to the kernel.
 *
 * As it is, this scheme should give priority to threads which were
 * blocked inside the kernel - waiting on an event, suspended on a
 * semaphore, etc. After that, it privileges shepherd threads, which
 * theoretically will attempt to enter the kernel to process incoming
 * packets, with high priority, and finally it deals with external
 * threads with attempt to lock the x-kernel. This may include
 * threads from anchor protocols, usually pushing messages down the stack.
 *
 * It is still to be verified if this scheme is fair enough, or if the
 * priotities policy may create starvation scenarios which may reduce
 * overall performance.
 */

typedef struct {
    int            count;
    pthread_cond_t cond;
} lock_info_t;

struct {
    xk_thread_t*    owner;
    int             access;
    lock_info_t     external;
    lock_info_t     internal;
    lock_info_t     shepherd;
    pthread_mutex_t mutex;
} xk_lock;

pthread_mutex_t* xk_mutex = &(xk_lock.mutex);

static Map   thread_map;

static void* thread_stub( void* arg );

main_thread_t main_thread_desc;

void       thread_map_dump( void );
static int thread_dump( pthread_t* pthread, xk_thread_t* thread, int* tcount );

#ifdef XK_TRANSITION_TRACE
static char* thread_status_str( thread_status_t status );
#endif /* XK_TRANSITION_TRACE */

static char* thread_type_str( xk_thread_t* thread );

xk_thread_t* xk_lock_owner(void);

xk_thread_t*
xk_lock_owner(void)
{
    return xk_lock.owner;
}

static xk_thread_t xk_thread_error;

xk_thread_t*
threadSelf( void )
{
    pthread_t    this_pthread = pthread_self();
    xk_thread_t* this_xk_thread = NULL;

    if(mapResolve(thread_map,&this_pthread,(void**)&this_xk_thread)!=XK_SUCCESS)
    {
	this_xk_thread = &xk_thread_error;
    }

    return this_xk_thread;
}

/*
 * threadInit is called by main, and so is guaranteed to be run by the
 * main thread. It uses that to guarantee the main thread is blocked
 * forever, so that it will never return. If it returned, _exit would
 * abort all other p-threads!
 *
 * New version (04/97): since we want this thread to stay around forever
 * we can use it to do some reasonable work while at it. To be precise,
 * use the main thread to run the device driver receiving thread, which,
 * by the way, should never end, anyway!
 */

void
threadInit( ThreadFunc init, void* arg )
{
    int             pthread_stacksize;
    xk_thread_t     this_xk_thread;

    xTrace0(threads,TR_FUNCTIONAL_TRACE,"threadInit: called");
    
    SET_THREAD_STATUS( (&this_xk_thread), XK_RELEASED );
#ifdef XK_TRANSITION_TRACE
    this_xk_thread.tinfo_cnt = -1;
#endif /* XK_TRANSITION_TRACE */
    this_xk_thread.func   = (ThreadFunc)threadInit;
    this_xk_thread.arg    = init;
    this_xk_thread.type   = XK_MAIN;
    this_xk_thread.pthread = pthread_self();
    pthread_mutex_init(&(this_xk_thread.mutex),pthread_mutexattr_default);

    thread_map = mapCreate(113,sizeof(pthread_t));
    mapBind(thread_map,&(this_xk_thread.pthread),&this_xk_thread);

    xTrace2(threads,TR_NEVER,"Created: threadInit xk(0x%x)->pt(0x%x)",
            &this_xk_thread,this_xk_thread.pthread);

    xTrace1(threads,TR_ALWAYS,"threadInit: main thread is 0x%x",
                    threadSelf() );

    xk_lock.owner           = NULL;
    xk_lock.access          = 1;
    xk_lock.external.count = 0;
    xk_lock.internal.count = 0;
    xk_lock.shepherd.count = 0;
    pthread_mutex_init( &xk_lock.mutex,         pthread_mutexattr_default );
    pthread_cond_init(  &xk_lock.external.cond, pthread_condattr_default );
    pthread_cond_init(  &xk_lock.internal.cond, pthread_condattr_default );
    pthread_cond_init(  &xk_lock.shepherd.cond, pthread_condattr_default );
    xTrace1(threads,TR_ALWAYS,"threadInit: xk_mutex = 0x%x",xk_mutex);

    pthread_stacksize = pthread_attr_getstacksize(pthread_attr_default);

    xTrace1(threads,TR_DETAILED,"threadInit: pthreads default stack size is %d",
	            pthread_stacksize );
    if ( pthread_stacksize < DEFAULT_STACK_SIZE ) {
	xTrace1(threads,TR_DETAILED, "Increasing stack size to %d",
	        DEFAULT_STACK_SIZE );
	if (  pthread_attr_create( &xk_thread_attr ) < 0 ) {
	    xTrace0(threads,TR_ERRORS,"threadInit couldn't create thread attr");
	    xk_thread_attr = pthread_attr_default;
	} else {
	    pthread_attr_setstacksize(&xk_thread_attr,(long)DEFAULT_STACK_SIZE);
	}
    }
    else {
	xTrace1(threads,TR_FULL_TRACE,
	        "Keeping default stack size (%d)", pthread_stacksize );
	xk_thread_attr = pthread_attr_default;
    }

    xk_thread_count=0;
    p_thread_count=1;

    /*
     * Initialize and lock main_thread_desc before any other threads are
     * created, to make sure synchronization works as expected.
     */
    pthread_mutex_init( &main_thread_desc.mutex, pthread_mutexattr_default );
    pthread_cond_init(  &main_thread_desc.cond,  pthread_condattr_default );
    PTHREAD_MUTEX_LOCK( &main_thread_desc.mutex );
    main_thread_desc.func = NULL;
    main_thread_desc.arg  = NULL;

    xkThreadCreate( init, arg, XK_INIT );

    xTrace1(threads,TR_FULL_TRACE,
            "initThread: Main thread (0x%x) will now block until needed. Bye!",
	    threadSelf() );

    PTHREAD_COND_WAIT( &main_thread_desc.cond, &main_thread_desc.mutex );
    PTHREAD_MUTEX_UNLOCK( &main_thread_desc.mutex );

    if ( main_thread_desc.func == NULL ) {
	xTrace1(threads,TR_ERRORS,"Main thread (0x%x) unlocked, no function!",
	        threadSelf());
	Kabort("*** Main thread released with no function - this should never happen! ***");
    }
    /*
     * This should never return, since if the main thread return all others
     * will be terminated by the p-threads package
     */
    main_thread_desc.func(main_thread_desc.arg);

    xTrace1(threads,TR_ERRORS,"Main thread (0x%x) function returned!",
	    threadSelf());
    Kabort("*** Main thread function returned - this should never happen! ***");
}

void
xk_any_lock( xk_level_t level )
{
    xk_thread_t*    this_thread = threadSelf();
    char*           xk_lock_str;
    int*            xk_lock_count;
    pthread_cond_t* xk_lock_cond;

    switch (level) {
	case XK_EXTERNAL_LOCK:
				xk_lock_count = & (xk_lock.external.count);
				xk_lock_cond  = & (xk_lock.external.cond);
				xk_lock_str   = "external";
	break;
	case XK_INTERNAL_LOCK:
				xk_lock_count = & (xk_lock.internal.count);
				xk_lock_cond  = & (xk_lock.internal.cond);
				xk_lock_str   = "internal";
	break;
	case XK_SHEPHERD_LOCK:
				xk_lock_count = & (xk_lock.shepherd.count);
				xk_lock_cond  = & (xk_lock.shepherd.cond);
				xk_lock_str   = "shepherd";
	break;
	default:
	    xTrace1(threads,TR_ERRORS,"xk_lock: invalid level (%d)",level);
				xk_lock_count = & (xk_lock.external.count);
				xk_lock_cond  = & (xk_lock.external.cond);
				xk_lock_str   = "external";
	break;
	                       
    }

    xTrace2(threads,TR_FUNCTIONAL_TRACE,"xk_lock(%s): called by thread 0x%x",
            xk_lock_str, this_thread );

#ifdef USE_SEM_MUTEX
    PTHREAD_MUTEX_LOCK(&xk_lock.mutex);
#endif

    --xk_lock.access;
    if ( xk_lock.access < 0 ) {

	xTrace2(threads,TR_FULL_TRACE,"xk_lock(%s): xk_lock_access = %d",
		xk_lock_str, xk_lock.access );
	xTrace2(threads,TR_FULL_TRACE,
	        "xk_lock(%s): will wait on condition locked = %d",
		xk_lock_str, (*xk_lock_count)+1 );
	
	++(*xk_lock_count);
	PTHREAD_COND_WAIT( xk_lock_cond, &xk_lock.mutex );
	--(*xk_lock_count);

	xTrace2(threads,TR_FULL_TRACE,"xk_lock: back from condition, locked=%d",
		xk_lock_str, xk_lock_count);
    }
    SET_THREAD_STATUS( this_thread, XK_OWNER );
    xk_lock.owner = this_thread;

    xTrace1(threads,TR_FULL_TRACE,"xk_any_lock: xk_lock_access = %d",
            xk_lock.access );
    xTrace1(threads,TR_MAJOR_EVENTS,"xksl 0x%x got", this_thread );

#ifdef USE_SEM_MUTEX
    PTHREAD_MUTEX_UNLOCK(&xk_lock.mutex);

    xTrace1(threads,TR_FUNCTIONAL_TRACE,
            "xk_any_lock: thread 0x%x has the lock", this_thread );
#endif
}

void
xk_any_unlock(void)
{
    xk_thread_t* this_thread = threadSelf();

    xTrace1(threads,TR_FUNCTIONAL_TRACE,"xk_any_unlock: called by thread 0x%x",
            threadSelf() );

#ifdef USE_SEM_MUTEX
    PTHREAD_MUTEX_LOCK(&xk_lock.mutex);
#endif

    RESET_THREAD_STATUS( this_thread, XK_OWNER );

    xTrace1(threads,TR_MAJOR_EVENTS,"xkul 0x%x rel", threadSelf() );

    if(xk_lock.owner != this_thread) {
	xTrace2(threads,TR_ALWAYS,
	        "xk_any_unlock: owner(0x%x)!=this_thread(0x%x)",
	        xk_lock.owner, this_thread );
    }

    if ( xk_lock.access++ < 0 ) {
	xTrace1(threads,TR_FULL_TRACE,"xk_any_unlock: xk_lock.access = %d",
		xk_lock.access );
	if ( xk_lock.shepherd.count ) {
	    xTrace1(threads,TR_FULL_TRACE,"xk_any_unlock: signal %d sheps",
		    xk_lock.shepherd.count );
	    pthread_cond_signal(&xk_lock.shepherd.cond);
	} else if ( xk_lock.internal.count ) {
	    xTrace1(threads,TR_FULL_TRACE,"xk_any_unlock: signal %d int",
		    xk_lock.internal.count );
	    pthread_cond_signal(&xk_lock.internal.cond);
	} else {
	    xTrace0(threads,TR_FULL_TRACE,"xk_any_unlock: signal exts");
	    pthread_cond_signal(&xk_lock.external.cond);
	}
    }
    xTrace1(threads,TR_FULL_TRACE,"xk_any_unlock: xk_lock.access = %d",
	    xk_lock.access );

    xk_lock.owner = NULL;

#ifdef USE_SEM_MUTEX
    PTHREAD_MUTEX_UNLOCK(&xk_lock.mutex);
#endif

    xTrace1(threads,TR_FUNCTIONAL_TRACE,
            "xk_any_unlock: thread 0x%x released the lock", this_thread );
}

static void*
thread_stub( void* arg )
{
    xk_thread_t* this_thread = (xk_thread_t*) arg;
    int          xk_internal_thread = INTERNAL_THREAD(this_thread->type);
    char*        xk_thread_str = (xk_internal_thread)? "xk":"p";

    xk_thread_t* thread_self;

    xTrace2(threads,TR_DETAILED,"%s_thread_stub started as thread 0x%x",
		    xk_thread_str,
                    this_thread );

    PTHREAD_MUTEX_LOCK(&(this_thread->mutex));
    SET_THREAD_STATUS( this_thread, XK_RELEASED );
    PTHREAD_MUTEX_UNLOCK(&(this_thread->mutex));

    thread_self = threadSelf();
    if ( this_thread != thread_self ) {
	xTrace5(threads,TR_ALWAYS,
	        "%s_thread_stub: arg(0x%x,0x%x)!=threadSelf()(0x%x,0x%x)",
		xk_thread_str,
		this_thread,this_thread->pthread,
		thread_self,thread_self->pthread);
    }

    if ( xk_internal_thread ) {
	XK_LOCK( XK_INTERNAL_LOCK );
    }
    xTrace4(threads,TR_FULL_TRACE,"%s_thread_stub(0x%x): calling 0x%x(0x%x)",
                    xk_thread_str,
                    this_thread, this_thread->func, this_thread->arg );
    (this_thread->func)(this_thread->arg);
    xTrace4(threads,TR_FULL_TRACE,"%s_thread_stub(0x%x): 0x%x(0x%x) returned",
                    xk_thread_str,
                    this_thread, this_thread->func, this_thread->arg );
    if ( xk_internal_thread ) {
	XK_UNLOCK();
    }
    SET_THREAD_STATUS( this_thread, XK_DONE );

    if ( xk_internal_thread ) {
	xk_thread_count--;
    } else {
	p_thread_count--;
    }

    xTrace4(threads,TR_DETAILED,
            "%s_thread_stub(0x%x): returns, threads left: p=%d, xk=%d",
	    xk_thread_str,
            this_thread, p_thread_count, xk_thread_count );

    thread_self = threadSelf();
    if ( this_thread != thread_self ) {
	xTrace5(threads,TR_ALWAYS,
	        "%s_thread_stub: arg(0x%x,0x%x)!=threadSelf()(0x%x,0x%x)",
		xk_thread_str,
		this_thread,this_thread->pthread,
		thread_self,thread_self->pthread);
    }

    mapRemoveKey(thread_map,&(this_thread->pthread));

    xTrace2(threads,TR_NEVER,"Ended: xk(0x%x)->pt(0x%x)",
            this_thread,(this_thread->pthread));

    free(this_thread);

    return NULL;     /* Just to simplify type-checking */
}

void
xkThreadCreate( ThreadFunc func, void* arg, thread_type_t type )
{
    xk_thread_t* new_thread;
    xk_thread_t* this_thread = threadSelf();
    int          xk_internal_thread;
    char*        xk_thread_str;

    xk_internal_thread = INTERNAL_THREAD(type);
#ifdef XK_TRANSITION_TRACE
    new_thread->tinfo_cnt  = -1;
#endif /* XK_TRANSITION_TRACE */
    xk_thread_str      = (xk_internal_thread)? "xk":"p";

    new_thread = (xk_thread_t*) malloc(sizeof(xk_thread_t));
    new_thread->func   = func;
    new_thread->arg    = arg;
    new_thread->type   = type;
    SET_THREAD_STATUS( new_thread, XK_CREATED );

    pthread_mutex_init(&(new_thread->mutex),pthread_mutexattr_default);
    PTHREAD_MUTEX_LOCK(&(new_thread->mutex));

    xTrace2(threads,TR_FUNCTIONAL_TRACE,"%sThreadCreate called by thread 0x%x",
            xk_thread_str, this_thread );

    if ( pthread_create(&(new_thread->pthread),pthread_attr_default,
                        thread_stub,new_thread ) < 0 ){
	PTHREAD_MUTEX_UNLOCK(&(new_thread->mutex));
	free((char*)new_thread);
	xError("ThreadCreate: Unable to create new thread");
	return;
    }
    mapBind(thread_map,&(new_thread->pthread),new_thread);

    xTrace2(threads,TR_NEVER,"Created: xk(0x%x)->pt(0x%x)",
            new_thread,(new_thread->pthread));

    if ( xk_internal_thread ) {
	xk_thread_count++;
    } else {
	p_thread_count++;
    }
    xTrace5(threads,TR_EVENTS,
            "xkThreadCreate: thread 0x%x created %sthread 0x%x,func 0x%x(%x)",
	    this_thread, xk_thread_str,
	    new_thread, func, arg );
    PTHREAD_MUTEX_UNLOCK(&(new_thread->mutex));
    PTHREAD_DETACH( new_thread->pthread );
    xTrace1(threads,TR_FUNCTIONAL_TRACE,"ThreadCreate(0x%x) returning",
            this_thread );
}

void
threadStop(void)
{
    xk_thread_t* this_thread = threadSelf();

    xk_thread_count--;
    SET_THREAD_STATUS( this_thread, XK_DONE );
    xTrace2(threads,TR_EVENTS,"threadStop: called by 0x%x leaving %d xkthreads",
            this_thread, xk_thread_count );
    XK_UNLOCK();
    mapRemoveKey(thread_map,&(this_thread->pthread));
    xTrace2(threads,TR_ALWAYS,"Stopped: xk(0x%x)->pt(0x%x)",this_thread,(this_thread->pthread));
    free(this_thread);
    pthread_exit(&xk_thread_count); /* Not really used, just make gcc happy */
}

void
threadYield(void)
{
    xk_thread_t* this_thread = threadSelf();

    xTrace1(threads,TR_EVENTS,"threadYield: thread 0x%x yielding now",
            threadSelf());
    SET_THREAD_STATUS( this_thread, XK_YIELDED );
    XK_UNLOCK();
    xTrace1(threads,TR_MAJOR_EVENTS,"threadYield: thread 0x%x yielding now",
	    this_thread);
    pthread_yield();
    RESET_THREAD_STATUS( this_thread, XK_YIELDED );
    XK_LOCK( XK_INTERNAL_LOCK );
    xTrace1(threads,TR_EVENTS,"threadYield: thread 0x%x returning from yield",
            this_thread);
}

void
thread_map_dump( void )
{
    int thread_count = 0;
    /*
     * prints all information available about the state of kernel threads.
     */
     xTrace0(threads,TR_ALWAYS,"thread_map_dump: all threads will be printed");
     mapForEach( thread_map, (MapForEachFun) thread_dump, &thread_count);
     xTrace1(threads,TR_ALWAYS,"thread_map_dump: all threads (%d) were printed",
             thread_count);
}

static int
thread_dump( pthread_t* pthread, xk_thread_t* thread, int* tcount )
{
    int  i;
    char buf[10*1024], tinfo_str[10*1024];

    *tcount += 1;
    sprintf(buf,"0x%x(0x%x)=0x%x",thread->func,thread->arg);
    assert( *pthread == thread->pthread );
    xTrace6(threads,TR_ALWAYS,"thread 0x%x [%02d]: pthread 0x%x, func %s, "
                              "type %s, status %s",
                              thread, *tcount, thread->pthread, buf,
                              thread_type_str( thread ),
#ifdef XK_TRANSITION_TRACE
                              thread_status_str( thread->status )
#else 
                              "(no status trace)"
#endif /* XK_TRANSITION_TRACE */
			      );
#ifdef XK_TRANSITION_TRACE
    buf[0]=0;
    for (i=0;i<=thread->tinfo_cnt;++i) {
	sprintf(tinfo_str,"%s(0x%x)", thread_status_str( thread->tinfo[i].st ),
	                              thread->tinfo[i].ptr );
	strcat(buf,tinfo_str);
    }
    xTrace4(threads,TR_ALWAYS,"thread 0x%x [%02d]: tinfo(%d) = %s",
                              thread, *tcount, thread->tinfo_cnt, buf );
#endif /* XK_TRANSITION_TRACE */

    return MFE_CONTINUE;
}

#ifdef XK_TRANSITION_TRACE

static char*
thread_status_str( thread_status_t status )
{
    static char buffer[128];

#define CHECK_XKSTATUS( _ST ) if(status&PASTE(XK_,_ST)) strcat(buffer,#_ST " ");
#define CHECK_PTSTATUS( _ST ) if(status&PASTE(PT_,_ST)) strcat(buffer,#_ST " ");
#define CHECK_TWSTATUS( _ST ) if(status&PASTE(TW_,_ST)) strcat(buffer,"TW" #_ST " ");

    strcpy(buffer,"{ ");

    CHECK_XKSTATUS( CREATED );
    CHECK_XKSTATUS( RELEASED );
    CHECK_XKSTATUS( WAIT );
    CHECK_XKSTATUS( OWNER ); 
    CHECK_XKSTATUS( SEM_WAIT );
    CHECK_XKSTATUS( SEM_OWN );
    CHECK_XKSTATUS( DELAYED );
    CHECK_XKSTATUS( YIELDED );
    CHECK_XKSTATUS( SHEPNG );
    CHECK_XKSTATUS( SCHEDULED );
    CHECK_XKSTATUS( DONE );

    CHECK_PTSTATUS( LOCKING );
    CHECK_PTSTATUS( LOCKED );
    CHECK_PTSTATUS( COND_WAIT );
    CHECK_PTSTATUS( COND_RET );
    CHECK_PTSTATUS( UNLOCKING );
    CHECK_PTSTATUS( UNLOCKED );

    CHECK_TWSTATUS( LOCKING );
    CHECK_TWSTATUS( LOCKED );
    CHECK_TWSTATUS( COND_WAIT );

    strcat(buffer,"}");

    return buffer;
}

#endif /* XK_TRANSITION_TRACE */

static char*
thread_type_str( xk_thread_t* thread )
{
    char* unknown = "UNKNOWN (0x????????)";
    switch( thread->type ) {
        case XK_EXTERNAL:   return "EXTERNAL";
        case XK_INTERNAL:   return "INTERNAL";
        case XK_EVENT:      return "EVENT";
        case XK_SHEPHERD:   return "SHEPHERD";
        case XK_SOFT_CLOCK: return "SOFT_CLOCK";
        case XK_HB:         return "HB";
        case XK_MAIN:       return "MAIN";
        case XK_INIT:       return "INIT";
        case XK_DRIVER:     return "DRIVER";
        default:            
	                    sprintf(unknown,"UNKNOWN (0x%08x)",thread->type);
			    return unknown;
    }
    return NULL;
}

