#ifndef _THREAD_H_
#define _THREAD_H_ 1
#if defined(__cplusplus)
extern "C" { 
#endif

AUTOLIB(thread)

/*
 * basic procs and threads
 */
int		proccreate(void (*f)(void *arg), void *arg, unsigned int stacksize);
int		threadcreate(void (*f)(void *arg), void *arg, unsigned int stacksize);
void		threadexits(char *);
void		threadexitsall(char *);
void		threadsetname(char*, ...);
void		threadsetstate(char*, ...);
char		*threadgetname(void);
int			threadyield(void);
int			threadidle(void);
void		_threadready(_Thread*);
void		_threadswitch(void);
void		_threadsetsysproc(void);
void		_threadsleep(Rendez*);
_Thread	*_threadwakeup(Rendez*);
#define	yield		threadyield
int		threadid(void);
void		_threadpin(void);
void		_threadunpin(void);

/*
 * I am tired of making this mistake.
 */
#define	exits	do_not_call_exits_in_threaded_programs
#define	_exits	do_not_call__exits_in_threaded_programs



/*
 * signals
 */
void		threadnotify(int(*f)(void*,char*), int);

/*
 * daemonize
 *
void	threaddaemonize(void);
 */

/*
 * per proc and thread data
 */
void		**procdata(void);
void		**threaddata(void);

/*
 * supplied by user instead of main.
 * mainstacksize is size of stack allocated to run threadmain
 */
void		threadmain(int argc, char *argv[]);
extern	int	mainstacksize;

/*
 * channel communication
 */
typedef struct Alt Alt;
typedef struct _Altarray _Altarray;
typedef struct Channel Channel;

enum
{
	CHANEND,
	CHANSND,
	CHANRCV,
	CHANNOP,
	CHANNOBLK
};

struct Alt
{
	Channel		*c;
	void		*v;
	uint		op;
	_Thread		*thread;
};

struct _Altarray
{
	Alt			**a;
	uint		n;
	uint		m;
};

struct Channel
{
	uint			bufsize;
	uint			elemsize;
	uchar		*buf;
	uint			nbuf;
	uint			off;
	_Altarray	asend;
	_Altarray	arecv;
	char			*name;
};

/* [Edit .+1,./^$/ |cfn -h $PLAN9/src/libthread/channel.c] */
int		chanalt(Alt *alts);
Channel*	chancreate(int elemsize, int elemcnt);
void		chanfree(Channel *c);
int		channbrecv(Channel *c, void *v);
void*		channbrecvp(Channel *c);
ulong		channbrecvul(Channel *c);
int		channbsend(Channel *c, void *v);
int		channbsendp(Channel *c, void *v);
int		channbsendul(Channel *c, ulong v);
int		chanrecv(Channel *c, void *v);
void*		chanrecvp(Channel *c);
ulong		chanrecvul(Channel *c);
int		chansend(Channel *c, void *v);
int		chansendp(Channel *c, void *v);
int		chansendul(Channel *c, ulong v);
void		chansetname(Channel *c, char *fmt, ...);

#define	alt		chanalt
#define	nbrecv	channbrecv
#define	nbrecvp	channbrecvp
#define	nbrecvul	channbrecvul
#define	nbsend	channbsend
#define	nbsendp	channbsendp
#define	nbsendul	channbsendul
#define	recv		chanrecv
#define	recvp	chanrecvp
#define	recvul	chanrecvul
#define	send		chansend
#define	sendp	chansendp
#define	sendul	chansendul

/*
 * reference counts
 */
typedef struct Ref	Ref;

struct Ref {
	Lock lock;
	long ref;
};

long		decref(Ref *r);
long		incref(Ref *r);

/*
 * slave i/o processes
 */
typedef struct Ioproc Ioproc;

/* [Edit .+1,/^$/ |cfn -h $PLAN9/src/libthread/io*.c] */
void		closeioproc(Ioproc *io);
long		iocall(Ioproc *io, long (*op)(va_list*), ...);
int		ioclose(Ioproc *io, int fd);
int		iodial(Ioproc *io, char *addr, char *local, char *dir, int *cdfp);
void		iointerrupt(Ioproc *io);
int		ioopen(Ioproc *io, char *path, int mode);
Ioproc*		ioproc(void);
long		ioread(Ioproc *io, int fd, void *a, long n);
int		ioread9pmsg(Ioproc*, int, void*, int);
long		ioreadn(Ioproc *io, int fd, void *a, long n);
int		iorecvfd(Ioproc *, int);
int		iosendfd(Ioproc*, int, int);
int		iosleep(Ioproc *io, long n);
long		iowrite(Ioproc *io, int fd, void *a, long n);

/*
 * exec external programs
 */
void		threadexec(Channel*, int[3], char*, char *[]);
void		threadexecl(Channel*, int[3], char*, ...);
int		threadspawn(int[3], char*, char*[]);
int		threadspawnd(int[3], char*, char*[], char*);
int		threadspawnl(int[3], char*, ...);
Channel*	threadwaitchan(void);

/*
 * alternate interface to threadwaitchan - don't use both! 
 */
Waitmsg*	procwait(int pid);

#if defined(__cplusplus)
}
#endif
#endif	/* _THREADH_ */
