#include <u.h>
#include <libc.h>
#include <thread.h>

typedef struct Waiter Waiter;

struct {
	QLock lk;
	Waitmsg **msg;
	int nmsg;
	int muxer;
	Waiter *head;
} waiting;

struct Waiter
{
	Rendez r;
	Waitmsg *msg;
	int pid;
	Waiter *next;
	Waiter *prev;
};

/* see src/libmux/mux.c */
Waitmsg*
procwait(int pid)
{
	Waiter *w;
	Waiter me;
	Waitmsg *msg;
	int i;
	
	memset(&me, 0, sizeof me);
	me.pid = pid;
	me.r.l = &waiting.lk;
	
	qlock(&waiting.lk);
	for(i=0; i<waiting.nmsg; i++){
		if(waiting.msg[i]->pid == pid){
			msg = waiting.msg[i];
			waiting.msg[i] = waiting.msg[--waiting.nmsg];
			qunlock(&waiting.lk);
			return msg;
		}
	}
	me.next = waiting.head;
	me.prev = nil;
	if(me.next)
		me.next->prev = &me;
	waiting.head = &me;
	while(waiting.muxer && me.msg==nil)
		rsleep(&me.r);

	if(!me.msg){
		if(waiting.muxer)
			abort();
		waiting.muxer = 1;
		while(!me.msg){
			qunlock(&waiting.lk);
			msg = recvp(threadwaitchan());
			qlock(&waiting.lk);
			if(msg == nil)	/* shouldn't happen */
				break;
			for(w=waiting.head; w; w=w->next)
				if(w->pid == msg->pid)
					break;
			if(w){
				if(w->prev)
					w->prev->next = w->next;
				else
					waiting.head = w->next;
				if(w->next)
					w->next->prev = w->prev;
				me.msg = msg;
				rwakeup(&w->r);
			}else{
				waiting.msg = realloc(waiting.msg, (waiting.nmsg+1)*sizeof waiting.msg[0]);
				if(waiting.msg == nil)
					sysfatal("out of memory");
				waiting.msg[waiting.nmsg++] = msg;
			}
		}
		waiting.muxer = 0;
		if(waiting.head)
			rwakeup(&waiting.head->r);
	}
	qunlock(&waiting.lk);
	if (me.msg->pid < 0) {
		free(me.msg);
		me.msg = 0;
	}
	return me.msg;
}
