/*
 * This needs to be callable from a signal handler, so it has been
 * written to avoid locks.  The only lock is the one used to acquire
 * an entry in the table, and we make sure that acquiring is done
 * when not in a handler.  Lookup and delete do not need locks.
 * It's a scan-forward hash table.  To avoid breaking chains, 
 * T ((void*)-1) is used as a non-breaking nil.
 */

#include <u.h>
#include <libc.h>
#include "9proc.h"

enum { PIDHASH = 1021 };

#define T ((void*)-1)
static Uproc *alluproc[PIDHASH];
static int allupid[PIDHASH];
static Lock uproclock;

void
_clearuproc(void)
{
	int i;

	/* called right after fork - no locking needed */
	for(i=0; i<PIDHASH; i++)
		if(alluproc[i] != T && alluproc[i] != 0)
			free(alluproc[i]);
	memset(alluproc, 0, sizeof alluproc);
	memset(allupid, 0, sizeof allupid);
}
		
Uproc*
_p9uproc(int inhandler)
{
	int i, h, pid;
	Uproc *up;

	/* for now, assume getpid is fast or cached */
	pid = getpid();

	/*
	 * this part - the lookup - needs to run without locks
	 * so that it can safely be called from within the notify handler.
	 * notify calls _p9uproc, and fork and rfork call _p9uproc
	 * in both parent and child, so if we're in a signal handler,
	 * we should find something in the table.
	 */
	h = pid%PIDHASH;
	for(i=0; i<PIDHASH; i++){
		up = alluproc[h];
		if(up == nil)
			break;
		if(allupid[h] == pid)
			return up;
		if(++h == PIDHASH)
			h = 0;
	}

	if(inhandler){
		fprint(2, "%s: did not find uproc for pid %d in signal handler\n", argv0, pid);
		abort();	
	}

	/* need to allocate */
	while((up = mallocz(sizeof(Uproc), 1)) == nil)
		sleep(1000);

	/* fprint(2, "alloc uproc for pid %d\n", pid); */
	up->pid = pid;
	lock(&uproclock);
	h = pid%PIDHASH;
	for(i=0; i<PIDHASH; i++){
		if(alluproc[h]==T || alluproc[h]==nil){
			alluproc[h] = up;
			allupid[h] = pid;
			unlock(&uproclock);
			return up;
		}
		if(++h == PIDHASH)
			h = 0;
	}
	unlock(&uproclock);

	/* out of pids! */
	sysfatal("too many processes in uproc table");
	return nil;
}

void
_p9uprocdie(void)
{
	Uproc *up;
	int pid, i, h;

	pid = getpid();
	/* fprint(2, "reap uproc for pid %d\n", pid); */
	h = pid%PIDHASH;
	for(i=0; i<PIDHASH; i++){
		up = alluproc[h];
		if(up == nil)
			break;
		if(up == T)
			continue;
		if(allupid[h] == pid){
			up = alluproc[h];
			alluproc[h] = T;
			free(up);
			allupid[h] = 0;
		}
	}
}
