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

int _threadnonotes;

/*
 * Threadkillgrp arranges the death of a particular thread group.
 * On Plan 9 we used to send the note kilgrp%d to the entire process group,
 * but we don't have that much flexibility in the signals on other systems.
 * Instead, we iterate over all threads and mark the ones that are to die.
 */
void
threadkillgrp(int grp)
{
	Tproc *p;
	Thread *t;

	lock(&_threadpq.lk);
	for(p=_threadpq.head; p; p=p->next){
		lock(&p->threadlk);
		for(t=p->threads.head; t; t=t->nextt)
			if(t->grp == grp)
				t->exiting = 1;
		unlock(&p->threadlk);
	}
	unlock(&_threadpq.lk);
	_threadsignal();
}

/*
 * On Plan 9 we used to send kilthr%d to the entire process group.
 * See comment for threadkillgrp.
 */
void
threadkill(int id)
{
	Tproc *p;
	Thread *t;
	int selfthread, selfproc;

	lock(&_threadpq.lk);
	for(p=_threadpq.head; p; p=p->next){
		lock(&p->threadlk);
		for(t=p->threads.head; t; t=t->nextt)
			if(t->id == id)
				goto Found;
		unlock(&p->threadlk);
	}
	unlock(&_threadpq.lk);
	_threaddebug(DBGNOTE, "Can't find thread to kill");
	return;

Found:
	lock(&_threndezlock);
	if(t->state == Rendezvous){
		_threaddebug(DBGNOTE, "Thread was in rendezvous");
		assert(_threadgetqbytag(&_threndez, t->tag) == t);
	}
	t->exiting = 1;
	selfproc = p == _threadgetproc();
	selfthread = t == _threadgetthr();
	unlock(&_threndezlock);
	unlock(&p->threadlk);
	unlock(&_threadpq.lk);
	if(selfproc){
		_threaddebug(DBGNOTE, "Thread in same proc");
		if(selfthread){
			_threaddebug(DBGNOTE, "Threadkill: killing self");
			threadexits("threadicide");
		}
		_freethread(t);
		return;
	}
	/*
	 * We could signal just the relevant process here, but
	 * that would require holding p->threadlock or _threadpq.lock
	 * until after sending the signal, so that the p structure doesn't
	 * get freed out from under us.  Just signal everyone instead.
	 */
	_threadsignal();
}

/*
 * Isolate ourselves from outside notes by killing off the passer.
 */
void
threadnonotes(void)
{
	_threadnonotes = 1;
	_threadsignalpasser();
}
