#include "threadimpl.h"
#include "BSD.c"

#include <dlfcn.h>

struct thread_tag {
	struct thread_tag	*next;
	spinlock_t		l;
	volatile int		key;
	void			*data;
};

static spinlock_t mlock;
static spinlock_t dl_lock;
static spinlock_t tag_lock;
static struct thread_tag *thread_tag_store = nil;
static uint nextkey = 0;

void
_thread_malloc_lock(void)
{
	_spinlock(&mlock);
}

void
_thread_malloc_unlock(void)
{
	_spinunlock(&mlock);
}

void
_thread_malloc_init(void)
{
}

/*
 * for ld.so
 */
void
_thread_dl_lock(int t)
{
	if(t)
		_spinunlock(&dl_lock);
	else
		_spinlock(&dl_lock);
}

/*
 * for libc
 */
static void
_thread_tag_init(void **tag)
{
	struct thread_tag *t;

	_spinlock(&tag_lock);
	if(*tag == nil) {
		t = malloc(sizeof (*t));
		if(t != nil) {
			memset(&t->l, 0, sizeof(t->l));
			t->key = nextkey++;
			*tag = t;
		}
	}
	_spinunlock(&tag_lock);
}

void
_thread_tag_lock(void **tag)
{
	struct thread_tag *t;

	if(*tag == nil)
		_thread_tag_init(tag);
	t = *tag;
	_spinlock(&t->l);
}

void
_thread_tag_unlock(void **tag)
{
	struct thread_tag *t;

	if(*tag == nil)
		_thread_tag_init(tag);
	t = *tag;
	_spinunlock(&t->l);
}

static void *
_thread_tag_insert(struct thread_tag *t, void *v)
{
	t->data = v;
	t->next = thread_tag_store;
	thread_tag_store = t;
	return t;
}

static void *
_thread_tag_lookup(struct thread_tag *tag, int size)
{
	struct thread_tag *t;
	void *p;

	_spinlock(&tag->l);
	for(t = thread_tag_store; t != nil; t = t->next)
		if(t->key == tag->key)
			break;
	if(t == nil) {
		p = malloc(size);
		if(p == nil) {
			_spinunlock(&tag->l);
			return nil;
		}
		_thread_tag_insert(tag, p);
	}
	_spinunlock(&tag->l);
	return tag->data;
}

void *
_thread_tag_storage(void **tag, void *storage, size_t n, void *err)
{
	struct thread_tag *t;
	void *r;

	if(*tag == nil)
		_thread_tag_init(tag);
	t = *tag;

	r = _thread_tag_lookup(t, n);
	if(r == nil)
		r = err;
	else
		memcpy(r, storage, n);
	return r;
}

void
_pthreadinit(void)
{
	__isthreaded = 1;
	dlctl(nil, DL_SETTHREADLCK, _thread_dl_lock);
	signal(SIGUSR2, sigusr2handler);
}