#include <pthread.h>
#include <string.h>

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

pthread_mutex_t pm_rendlock;

enum
{
	NHLOG	= 7,
	NHASH	= (1<<NHLOG)
};

typedef struct Tag Tag;
struct Tag
{
	ulong	tag;
	ulong	val;
	Tag*	hash;
	Tag*	free;
	pthread_cond_t cv;
};

static	Tag*	ht[NHASH];
static	Tag*	ft;

ulong
rendezvous(ulong tag, ulong value)
{
	int h;
	int err;
	ulong rval;
	Tag *t, *f, **l;
	Pmspl s;

	if(tag == 0)
		pm_panic("rendezvous zero tag");

	h = tag & (NHASH-1);

	s = pm_splhi();
	if((err = pthread_mutex_lock(&pm_rendlock)) != 0){
		char buf[512];
		pm_snprint(buf, sizeof buf, "pthread_mutex_lock failed: %s", strerror(err)); 
		pm_panic(buf);
	}
pm_fprint(2, "%d entering rend-pthread\n",getpid());

	l = &ht[h];
	for(t = *l; t; t = t->hash) {
		if(t->tag == tag) {
			rval = t->val;
			t->val = value;
			t->tag = 0;
pm_fprint(2, "%d leaving rend-pthread to sleep, perchance to dream\n", getpid());
			if(pthread_mutex_unlock(&pm_rendlock))
				pm_panic("pthread_mutex_unlock failed");
			pm_splx(s);
			if(pthread_cond_signal(&(t->cv)))
				pm_panic("pthread_cond_signal failed");
			return rval;		
		}
	}

	t = ft;
	if(t == 0)
		t = malloc(sizeof(Tag));
	else
		ft = t->free;

	t->tag = tag;
	t->val = value;
	t->hash = *l;
	if(pthread_cond_init(&(t->cv), NULL))
		pm_panic("pthread_cond_init failed");
	*l = t;

	while(t->tag){
		pthread_mutex_unlock(&pm_rendlock);
		pm_splx(s);
		pthread_cond_wait(&(t->cv), NULL);
		s = pm_splhi();
		pthread_mutex_lock(&pm_rendlock);
	}

	rval = t->val;
	for(f = *l; f; f = f->hash){
		if(f == t) {
			*l = f->hash;
			break;
		}
		l = &f->hash;
	}
	t->free = ft;
	ft = t;
pm_fprint(2, "%d leaving rend-pthread\n",getpid());
	if(pthread_mutex_unlock(&pm_rendlock))
		pm_panic("pthread_mutex_unlock failed");
	pm_splx(s);

	return rval;
}
