#include <9pm/u.h>
#include <9pm/libc.h>
#include <9pm/ns.h>
#include <9pm/thread.h>
#include <9pm/threadimpl.h>

char *_threadexitsallstatus;

void
threadexits(char *exitstr)
{
	Thread *t, *new;
	Tproc *p;
	Channel *c;

	_threaddebug(DBGTHRD|DBGKILL, "Exitthread()");
	p = _threadgetproc();
	t = _threadgetthr();
	if (p->nthreads > 1) {
		t->exiting = 1;
		p->nthreads--;
		lock(&_threndezlock);
		while ((new = _threadgetq(&p->runnable)) == nil) {
			_threaddebug(DBGTHRD, "Nothing left to run");
			/* called with rendezlock held */
			p->blocked = 1;
			unlock(&_threndezlock);
			if ((new = (Thread *)rendezvous((ulong)p, 0)) != (Thread *)~0) {
				threadassert(!p->blocked);
				threadassert(new->proc == p);
				p->curthread = new;
				new->state = Running;
				/* switch to new thread, pass it `t' for cleanup */
				new->garbage = t;
				longjmp(new->env, (int)t);
				/* No return */
			}
			_threaddebug(DBGNOTE|DBGTHRD, "interrupted");
			lock(&_threndezlock);
		}
		unlock(&_threndezlock);
		_threaddebug(DBGTHRD, "Yield atexit to %d.%d", p->pid, new->id);
		p->curthread = new;
		new->state = Running;
		if (new->exiting)
			threadexits(nil);
		/* switch to new thread, pass it `t' for cleanup */
		new->garbage = t;
		longjmp(new->env, (int)t);
		/* no return */
	}
	/*
	 * thewaitchan confounds exiting the entire program, so handle it
	 * carefully; store exposed global in local variable c.
	 */
	if(!p->nowait && (c = _threadwaitchan) != nil) {
		Waitmsg *w;
		long t[4];

		/* create a Waitmsg out of whole cloth; this proc was created RFNOWAIT */

		w = _threadmalloc(sizeof(Waitmsg)+(exitstr==nil? 0 : strlen(exitstr)+1), 0);
		w->pid = p->pid;
		w->tm[2] = _threadtimes(t);	/* real */
		w->tm[0] = t[0]+t[2];	/* user + child user */
		w->tm[1] = t[1]+t[3];	/* sys + child sys */
		w->msg = "";
			
		if(exitstr != nil){
			w->msg = (char*)(&w[1]);
			strcpy(w->msg, exitstr);
		}
		_threaddebug(DBGCHLD, "In thread %s: sending exit status %s for %d",
			p->curthread->cmdname, exitstr, p->pid);
		sendp(c, w);
	}
	_threaddebug(DBGPROC, "Exiting p=%p", p);
	t->exiting = 1;
	if(exitstr == nil)
		p->exitstr[0] = '\0';
	else
		utfecpy(p->exitstr, p->exitstr+sizeof p->exitstr, exitstr);
	p->nthreads--;
	/* clean up and exit */
	longjmp(p->oldenv, DOEXIT);
}

void
threadexitsall(char *status)
{
	Channel *c;
	Waitmsg *w;

	if ((c = _threadwaitchan) != nil) {
		_threadwaitchan = nil;
		while ((w = nbrecvp(c)) != nil){
			/* Unblock threads previously exited,
			* sending on thewaitchan, not yet waited for
			*/
			free(w);
		}
	}
	if(status == nil)
		status="";
	_threadexitsallstatus = status;
	_threadsignalpasser();
	_threadsignal();
	_osexitsall(status);
	exits(status);
}
