typedef struct Tproc	Tproc;
typedef struct Thread	Thread;
typedef struct Tqueue	Tqueue;

typedef enum
{
	Nascent,
	Running,
	Runnable,
	Rendezvous,
} State;
	
typedef enum
{
	Callnil,
	Callalt,
	Callsnd,
	Callrcv,
} Callstate;

enum
{
	DOEXEC = 1,
	DOEXIT = 2,
	DOPROC = 3,
};

struct Tqueue		/* Thread queue */
{
	Lock	lk;
	Thread	*head;
	Thread	*tail;
};

struct Thread
{
	Lock		lk;		/* protects thread data structure */
	int			id;		/* thread id */
	int 		grp;		/* thread group */
	State		state;		/* state of thread */
	int			exiting;	/* should it die? */
	Callstate	call;		/* which `system call' is current */
	char		*cmdname;	/* ptr to name of thread */
	Thread		*next;		/* next on queue (run, rendezvous) */
	Thread		*nextt;		/* next on list of all theads */
	Tproc		*proc;		/* proc of this thread */
	ulong		tag;		/* rendez-vous tag */
	Alt			*alt;		/* pointer to alt structure */
	ulong		value;		/* rendez-vous value */
	Thread		*garbage;	/* ptr to thread to clean up */
	jmp_buf		env;		/* jump buf for launching or switching threads */
	uchar		*stk;		/* top of stack (lowest address of stack) */
	uint		stksize;	/* stack size */

	ulong	udata;		/* User per-thread data pointer */
};

typedef struct Execproc
{
	char	*file;
	char	**arg;
	char	data[4096];
} Execproc;

struct Tproc
{
	Proc	sysproc;

	int	nowait;
	int	pid;
	int	rforkflag;		/* used at creation time */

	jmp_buf	oldenv;			/* jump buf for returning to original stack */

	int	nthreads;
	Lock	threadlk;		/* protects Tqueue threads */
	Tqueue	threads;		/* All threads of this proc */
	Tqueue	runnable;		/* Runnable threads */
	Thread	*curthread;		/* Running thread */

	int	blocked;		/* In a rendezvous */
	uint	nextID;			/* ID of most recently created thread */
	Tproc	*next;			/* linked list of Procs */

	void	*arg;			/* passed between shared and unshared stk */
	char	exitstr[ERRMAX];	/* used by threadexits to avoid malloc */

	ulong	udata;			/* User per-proc data pointer */

	Execproc	execproc;	/* arguments for exec */

	void	*impl;			/* implementation pointer */
};

struct Pqueue		/* Proc queue */
{
	Lock	lk;
	Tproc	*head;
	Tproc	*tail;
};

extern Tqueue _threndez;
extern Lock _threndezlock;
extern struct Pqueue _threadpq;	/* Linked list of procs */
extern int _threaddebuglevel;
extern Channel *_threadwaitchan;
extern char *_threadexitsallstatus;
extern int _threadnonotes;
extern int mainstacksize;

ulong	_threadrendezvous(ulong, ulong);
void	*_threadmalloc(long size, int);
int _threadfdcoherence(void);
void _freeproc(Tproc*);
void _osfreeproc(Tproc*);
void _freethread(Thread*);
Thread *_threadgetqbytag(Tqueue*, ulong);
void _threadputq(Tqueue*, Thread*);
Thread *_threadgetq(Tqueue*);
void _threadlauncher(void (*f)(void*), void*);
void _mainlauncher(int, char*[]);
Thread *_threadgetthr(void);
Tproc *_threadgetproc(void);
void _threadkillall(void);
long _threadtimes(long*);
void _threadsignal(void);
void _threadsignalpasser(void);
ulong _threadswtch(jmp_buf, jmp_buf, ulong);
void _threadsysfatal(char*, va_list);
Tproc *_newproc(void (*)(void*), void*, uint, char*, int, int);
void _osexitsall(void*);
void _osnewproc(Tproc*);
void _threadinitstack(Thread*, void(*)(void*), void*);
void _procexecwait(int);

static void		initproc(void (*)(ulong, int, char*[]), int, char*[], uint);

void _threaddebug(ulong, char *fmt, ...);
void _threadfatal(char *fmt, ...);
void _threadassert(char *s, char *file, int line);

#define DBGAPPL	(1 << 0)
#define DBGTHRD	(1 << 16)
#define DBGCHAN	(1 << 17)
#define DBGREND	(1 << 18)
#define DBGKILL	(1 << 19)
#define DBGNOTE	(1 << 20)
#define DBGQUE	(1 << 21)
#define DBGCHLD	(1 << 22)
#define DBGPROC	(1 << 23)

#define	threadassert(x)	if(x){;}else _threadassert(# x, __FILE__, __LINE__)
