/*     
 * $RCSfile: xk_thread.h,v $
 *
 * x-kernel v3.3
 *
 * Copyright (c) 1993,1991,1990,1996  Arizona Board of Regents
 *
 * $Log: xk_thread.h,v $
 * Revision 1.5  1997/03/17 06:41:22  dorgival
 * Cleanup, rearrangements; now seems to work without deadlocks.
 *
 * Revision 1.4  1997/03/17  01:35:18  dorgival
 * Improved tracing with macros around pthread_mutex/cond calls
 *
 * Revision 1.3  1997/03/16  18:31:14  dorgival
 * Changed the thread creation interface
 * Changed xk_lock structure
 *
 * Revision 1.2  1997/03/12 23:00:18  dorgival
 * Changes in the lock/unlock routine names/prototypes
 *
 * Revision 1.1  1997/03/11 20:47:08  dorgival
 * Initial revision
 *
 *
 */

#ifndef __threads_h__
#define __threads_h__

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

typedef void*			(*ThreadFunc)(void* arg);

typedef struct {
    pthread_mutex_t mutex;
    pthread_cond_t  cond;
    void            (*func)(void*);
    void*           arg;
} main_thread_t;

extern main_thread_t main_thread_desc;

typedef enum {
    XK_EXTERNAL,
    XK_INTERNAL,
    XK_EVENT,
    XK_SHEPHERD,
    XK_SOFT_CLOCK,
    XK_HB,
    XK_DRIVER,
    XK_INIT,
    XK_MAIN
} thread_type_t;

#define INTERNAL_THREAD(t) (((t)==XK_INTERNAL)||((t)==XK_EVENT)||((t)==XK_INIT))

/* #defien XK_TRANSITION_TRACE */

#ifdef XK_TRANSITION_TRACE

typedef enum {
    XK_CREATED     = 1<<0,
    XK_RELEASED    = 1<<1,
    XK_WAIT        = 1<<2,
    XK_OWNER       = 1<<3,
    XK_SEM_WAIT    = 1<<4,
    XK_SEM_OWN     = 1<<5,
    XK_DELAYED     = 1<<6,
    XK_YIELDED     = 1<<7,
    XK_SHEPNG      = 1<<8,
    XK_SCHEDULED   = 1<<9,
    XK_DONE        = 1<<16,

    PT_LOCKING     = 1<<17,
    PT_LOCKED      = 1<<18,
    PT_COND_WAIT   = 1<<19,
    PT_COND_RET    = 1<<20,
    PT_UNLOCKING   = 1<<21,
    PT_UNLOCKED    = 1<<22,

    TW_LOCKING     = 1<<23,
    TW_LOCKED      = 1<<24,
    TW_COND_WAIT   = 1<<25
} thread_status_t;

typedef struct {
    int   st;
    void* ptr;
} tinfo_t;

#endif /* XK_TRANSITION_TRACE */

typedef struct {
    pthread_t       pthread;
    ThreadFunc      func;
    void*           arg;
    thread_type_t   type;
    pthread_mutex_t mutex;

#ifdef XK_TRANSITION_TRACE
    thread_status_t status;
    tinfo_t         tinfo[100];
    int tinfo_cnt;
#endif /* XK_TRANSITION_TRACE */

} xk_thread_t;

#ifdef XK_TRANSITION_TRACE
#   define SET_THREAD_STATUS( _thread, _st )   (_thread)->status |=  (_st)
#   define RESET_THREAD_STATUS( _thread, _st ) (_thread)->status &= ~(_st)
#else
#   define SET_THREAD_STATUS( _thread, _st )   (0)
#   define RESET_THREAD_STATUS( _thread, _st ) (0)
#endif /* XK_TRANSITION_TRACE */

#define PTHREAD_COND_SIGNAL( cond )       pthread_cond_signal( cond )

#ifndef XK_TRANSITION_TRACE

#define PTHREAD_MUTEX_LOCK( mutex )       pthread_mutex_lock( mutex )
#define PTHREAD_COND_WAIT( cond, mutex )  pthread_cond_wait( cond, mutex )
#define PTHREAD_MUTEX_UNLOCK( mutex )     pthread_mutex_unlock( mutex )

#else

#define SET_INFO( self, _st, _ptr )                                         \
do {                                                                        \
    if ( (++self->tinfo_cnt) >= (sizeof(self->tinfo)/sizeof(tinfo_t)) ) {   \
	Kabort("Too many state changes!");                                  \
    }                                                                       \
    self->tinfo[self->tinfo_cnt].st  = _st;                                 \
    self->tinfo[self->tinfo_cnt].ptr = _ptr;                                \
} while (0)

#define CHG_INFO( self, _st )   (self)->tinfo[(self)->tinfo_cnt].st = _st
    

#define PTHREAD_MUTEX_LOCK( mutex_ptr )             \
do {                                                \
    xk_thread_t* self = threadSelf();               \
    SET_INFO( self, PT_LOCKING, mutex_ptr );        \
    pthread_mutex_lock( mutex_ptr );                \
    CHG_INFO( self, PT_LOCKED );                    \
} while(0)

#define PTHREAD_COND_WAIT( cond_ptr, mutex_ptr )    \
do {                                                \
    xk_thread_t* self = threadSelf();               \
    SET_INFO( self, PT_COND_WAIT, mutex_ptr );      \
    self->tinfo[self->tinfo_cnt].ptr = mutex_ptr;   \
    pthread_cond_wait( cond_ptr, mutex_ptr );       \
    CHG_INFO( self, PT_COND_RET );                  \
} while(0)

#define PTHREAD_MUTEX_UNLOCK( mutex_ptr )           \
do {                                                \
    xk_thread_t* self = threadSelf();               \
    SET_INFO( self, PT_UNLOCKING, mutex_ptr );      \
    pthread_mutex_unlock( mutex_ptr );              \
    CHG_INFO( self, PT_UNLOCKED );                  \
} while(0)

#endif /* XK_TRANSITION_TRACE */

extern xk_thread_t* threadSelf( void );
extern xk_thread_t* xk_lock_owner( void );

extern pthread_mutex_t* xk_mutex;

typedef enum { XK_EXTERNAL_LOCK,XK_INTERNAL_LOCK,XK_SHEPHERD_LOCK } xk_level_t;

extern void xk_any_lock( xk_level_t level );
extern void xk_any_unlock(void);

/*
 * USE_SEM_MUTEX adds a mutex to each semaphore to control access.
 * This has a flaw in it, since all threads already run with mutual
 * exclusion inside the x-kernel. Defining USE_SEM_MUTEX may cause
 * a race condition in which a mutexes to the x-kernel and the semaphore
 * are released/acquired out of order, resulting in deadlock.
 *
 * USE_SEM_MUTEX  should ***NEVER*** be defined. It is here just to
 * record the old code.
 */

#ifdef USE_SEM_MUTEX
#undef USE_SEM_MUTEX
#endif

#ifdef USE_SEM_MUTEX
#define XK_LOCK( type ) xk_any_lock( type )
#define XK_UNLOCK()     xk_any_unlock()
#else
#define XK_LOCK( type )              \
do {                                 \
    PTHREAD_MUTEX_LOCK(xk_mutex);    \
    xk_any_lock( type );             \
    PTHREAD_MUTEX_UNLOCK(xk_mutex);  \
} while (0)

#define XK_UNLOCK()                  \
do {                                 \
    PTHREAD_MUTEX_LOCK(xk_mutex);    \
    xk_any_unlock();                 \
    PTHREAD_MUTEX_UNLOCK(xk_mutex);  \
} while (0)

#endif

#define     xk_master_lock()     XK_LOCK( XK_EXTERNAL_LOCK )
#define     xk_master_unlock()   XK_UNLOCK()

extern void	threadInit (ThreadFunc init, void*   arg);
extern void	threadStop (void);
extern void	threadYield (void);
extern void	xkThreadCreate(ThreadFunc func, void* arg, thread_type_t type);

#endif /* __thread_h__ */
