/**********************************************************************
 
	Copyright (C) 2003 Hirohisa MORI <joshua@nichibun.ac.jp>
 
	This program is free software; you can redistribute it 
	and/or modify it under the terms of the GLOBALBASE 
	Library General Public License (G-LGPL) as published by 

	http://www.globalbase.org/
 
	This program is distributed in the hope that it will be 
	useful, but WITHOUT ANY WARRANTY; without even the 
	implied warranty of MERCHANTABILITY or FITNESS FOR A 
	PARTICULAR PURPOSE.

**********************************************************************/


#include	<stdlib.h>
#include	"memory_debug.h"
#include	"utils.h"
#include	"memory_routine.h"
#include	"task.h"
#include	"xl.h"

extern int test_cnt;

GC_THREAD *	gc_thread_hash[GC_THREAD_HASH_SIZE];


void gc_gb_sexp();


void
gc_gc_stack()
{
int i,j;
GC_THREAD * t;
GC_STACK * st;
GC_LIST * lst;
MEM * m;

int cnt,max;

	for ( i = 0 ; i < GC_THREAD_HASH_SIZE ; i ++ )
		for ( t = gc_thread_hash[i] ; t ; t = t->next )
			for ( st = t->stack ; st ; st = st->parent )
				for ( j = 0 ; j < GC_LIST_HASH_SIZE ; j ++ )
					for ( lst = st->hash[j] ; lst;
					      lst = lst->next ) {
						if ( lst->mem == 0 )
							continue;
						m = PT2MEM(lst->mem);
						if ( m->m_flags&M_STACK ) {
							lst->gc = 0;
							lst->mem = 0;
							continue;
						}
set_t_msg(10);
set_t_msg2(lst->gc);
						if ( lst->gc )
						   (*lst->gc)(lst->mem);
set_t_msg(11);
					}
}

void
print_stack(char * str,int dp)
{
int i,j;
GC_THREAD * t;
GC_STACK * st;
GC_LIST * lst;
MEM * m;
int tid;
int cnt,max;

	tid = get_tid();
	for ( i = 0 ; i < GC_THREAD_HASH_SIZE ; i ++ )
		for ( t = gc_thread_hash[i] ; t ; t = t->next ) {
			if ( t->tid != tid )
				continue;
			printf("%s-> ",str);
			cnt = 0;
			for ( st = t->stack ; st ; st = st->parent ) {
				printf("%s-",st->msg);
				cnt ++;
			}
			printf("\n");
			if ( dp >= 0 && cnt > dp )
				exit(1);
		}
}

void
print_th(char * str)
{
	printf("%s = %x (%x)\n",str,gc_thread_hash[8],&gc_thread_hash[8]);
}

GC_THREAD *
_search_me(int * tidp,int * keyp)
{
unsigned int tid;
unsigned int key;
GC_THREAD * ret;
	tid = get_tid();
	key = tid%GC_THREAD_HASH_SIZE;
	*tidp = tid;
	*keyp = key;
	ret = gc_thread_hash[key];
	for ( ; ret ; ret = ret->next )
		if ( ret->tid == tid )
			return ret;
	return 0;
}

int
get_stacktop_count()
{
GC_THREAD * t;
int tid;
int key;
GC_STACK * st;
GC_LIST * lst;
int cnt,i;
	t = _search_me(&tid,&key);
	if ( t == 0 )
		return 0;
	st = t->stack;
	if ( st == 0 )
		return 0;
	for ( ; st->parent ; st = st->parent );
	cnt = 0;
	for ( i = 0 ; i < GC_LIST_HASH_SIZE ; i ++ )
		for ( lst = st->hash[i] ; lst ; lst = lst->next )
			cnt ++;
	return cnt;
}

GC_THREAD *
_get_gc_my_thread()
{
GC_THREAD * ret;
int tid,key;

	ret = _search_me(&tid,&key);
	if ( ret )
		return ret;
	ret = d_alloc(sizeof(*ret),18);
	ret->tid = tid;
	ret->stack = 0;
	ret->next = gc_thread_hash[key];
	gc_thread_hash[key] = ret;
	return ret;
}

void
_delete_thread(GC_THREAD * t)
{
GC_THREAD ** tp;
unsigned int key;
	key = t->tid%GC_THREAD_HASH_SIZE;
	for ( tp = &gc_thread_hash[key] ; *tp ; tp = &(*tp)->next )
		if ( *tp == t ) {
			*tp = t->next;
			d_f_ree(t);
			return;
		}
}

GC_THREAD *
get_gc_my_thread()
{
GC_THREAD * ret;
	lock_mem();
	ret = _get_gc_my_thread();
	unlock_mem();
	return ret;
}

void
gc_check_empty()
{
int tid,key;
	lock_mem();
	if ( _search_me(&tid,&key) )
		er_panic("gc_check_empty:no empty");
	unlock_mem();
}

void
_push_stack(GC_THREAD * t,char * msg)
{
GC_STACK * st;
int i;

	st = d_alloc(sizeof(*st),19);
	for ( i = 0 ; i < GC_LIST_HASH_SIZE ; i ++ )
		st->hash[i] = 0;
	st->msg = copy_str(msg);
	st->parent = t->stack;
	t->stack = st;

}


void
_pop_stack(GC_THREAD * t)
{
GC_STACK * st;
int i;
unsigned int key;
GC_LIST * l2, * l3;
	if ( t->stack ) {
		st = t->stack;
		t->stack = st->parent;
		for ( i = 0 ; i < GC_LIST_HASH_SIZE ; i ++ ) {
			for ( ; st->hash[i] ; ) {
				l2 = st->hash[i];
				st->hash[i] = l2->next;
				d_f_ree(l2);
			}
		}
		d_f_ree(st->msg);
		d_f_ree(st);
	}
	if ( t->stack == 0 )
		_delete_thread(t);
}


void
gc_stack_top()
{
int tid,key;
GC_THREAD * t;
	t = _search_me(&tid,&key);
	if ( t == 0 )
		return;
	printf("STACK TOP %x\n",t->stack);
}

void
_insert_gc_list(GC_STACK * st,void * s,void (*gc)())
{
GC_LIST * lst;
unsigned int key;

	key = ((unsigned int)s)%GC_LIST_HASH_SIZE;

	for ( lst = st->hash[key] ; lst ; lst = lst->next )
		if ( lst->mem == s )
			return;

	lst = d_alloc(sizeof(*lst),20);
/*
if ( strcmp(st->msg,"tc_task2") == 0 )
printf("lst->mem = %x\n",s);
*/
	lst->mem = s;
	lst->gc = gc;
	lst->next = st->hash[key];
	st->hash[key] = lst;
}

void
_insert_gc_list_non_check(GC_STACK * st,void * s,void (*gc)())
{
GC_LIST * lst;
unsigned int key;

/*
if ( strcmp(st->msg,"tc_task2") == 0 )
printf("lst->mem = %x\n",s);
*/
	key = ((unsigned int)s)%GC_LIST_HASH_SIZE;

	lst = d_alloc(sizeof(*lst),20);
	lst->mem = s;
	lst->gc = gc;
	lst->next = st->hash[key];
	st->hash[key] = lst;
}

int
_delete_gc_list(GC_STACK * st,void * s)
{
GC_LIST ** lp;
GC_LIST * lst;
unsigned int key;
	key = ((unsigned int)s)%GC_LIST_HASH_SIZE;
	for ( lp = &st->hash[key]; *lp ; lp = &(*lp)->next )
		if ( (*lp)->mem == s ) {
			lst = *lp;
			*lp = lst->next;
			d_f_ree(lst);
			return 0;
		}
	return -1;
}

int
_delete_gc_list_pair(GC_STACK * st,void * s)
{
GC_LIST ** lp;
GC_LIST * lst;
unsigned int key;
XL_SEXP * sp;

	for ( key = 0 ; key < GC_LIST_HASH_SIZE; key ++ )
		for ( lp = &st->hash[key]; *lp ; lp = &(*lp)->next )
			if ( (*lp)->gc == gc_gb_sexp ) {
				sp = (*lp)->mem;
				if ( sp == s )
					continue;
				if ( sp->h.type != XLT_PAIR )
					continue;
				for ( ; sp && sp->h.type == XLT_PAIR &&
					sp != s;
						sp = sp->pair.cdr )
				if ( sp != s )
					continue;
				lst = *lp;
				*lp = lst->next;
				d_f_ree(lst);
				return 0;
			}
	return -1;
}


int
_gc_search_stack(GC_STACK * st,void * s)
{
GC_LIST ** lp;
GC_LIST * lst;
unsigned int key;
XL_SEXP * sp;
	key = ((unsigned int)s)%GC_LIST_HASH_SIZE;
	for ( lp = &st->hash[key]; *lp ; lp = &(*lp)->next )
		if ( (*lp)->mem == s ) {
			printf("STACK OK!!!!\n");
		}
	return -1;
}



void
_gc_set(GC_THREAD * t,void * s,void (*gc)())
{
GC_LIST * lst;
	if ( t->stack == 0 ) {
		_delete_thread(t);
		return;
	}
	_insert_gc_list(t->stack,s,gc);
}

void
gc_set_nl(void * s,void (*gc)())
{
int tid,key;
GC_THREAD * t;
	t = _search_me(&tid,&key);
	if ( t == 0 )
		return;
	_gc_set(t,s,gc);
}

void
gc_set(void * s,void (*gc)())
{
	lock_mem();
	_gc_set(_get_gc_my_thread(),s,gc);
	unlock_mem();
}

void
gc_delete_pair(void * s)
{
GC_THREAD * t;
int tid,key;
GC_STACK * st;
	t = _search_me(&tid,&key);
	lock_mem();
	for ( st = t->stack ; st ; st = st->parent )
		_delete_gc_list_pair(st,s);
	unlock_mem();
}

void
gc_search_stack(void * s)
{
GC_THREAD * t;
int tid,key;
GC_STACK * st;
	t = _search_me(&tid,&key);
	lock_mem();
	for ( st = t->stack ; st ; st = st->parent )
		_gc_search_stack(st,s);
	unlock_mem();
}

void
_gc_set_non_check(GC_THREAD * t,void * s,void (*gc)())
{
GC_LIST * lst;
	if ( t->stack == 0 ) {
		_delete_thread(t);
		return;
	}
	_insert_gc_list_non_check(t->stack,s,gc);
}

void
gc_set_nl_non_check(void * s,void (*gc)())
{
int tid,key;
GC_THREAD * t;
	t = _search_me(&tid,&key);
	if ( t == 0 )
		return;
	_gc_set_non_check(t,s,gc);
}

void
gc_set_non_check(void * s,void (*gc)())
{
	lock_mem();
	_gc_set_non_check(_get_gc_my_thread(),s,gc);
	unlock_mem();
}

void
_gc_delete(GC_THREAD * t,void * s)
{
GC_STACK * st;
	for ( st = t->stack ; st ; st = st->parent )
		_delete_gc_list(st,s);
}

void
_gc_push(GC_THREAD * t,void * s,void (*gc)(),char * msg)
{
GC_STACK * st;
	if ( s ) {
		for ( st = t->stack ; st ; st = st->parent )
			_delete_gc_list(st,s);
		_push_stack(t,msg);
		_insert_gc_list(t->stack,s,gc);
	}
	else {
		_push_stack(t,msg);
	}
}

void
_gc_pop(GC_THREAD * t,void * s,void (*gc)())
{
GC_STACK * st;
int tid,key;
	_pop_stack(t);
	if ( _search_me(&tid,&key) && s )
		_insert_gc_list(t->stack,s,gc);
}


void
gc_push(void * s,void (*gc)(),char * msg)
{
	lock_mem();
	_gc_push(_get_gc_my_thread(),s,gc,msg);
	unlock_mem();
}

void
gc_pop(void * s,void (*gc)())
{
	lock_mem();
	_gc_pop(_get_gc_my_thread(),s,gc);
	unlock_mem();
}


void
gc_delete(void * s)
{
	lock_mem();
	_gc_delete(_get_gc_my_thread(),s);
	unlock_mem();
}

void
gc_delete_nl(void * s)
{
	_gc_delete(_get_gc_my_thread(),s);
}

/*
void
resize_gc_mem_nl(void * old,void * new)
{
int i,j;
GC_THREAD * t;
GC_STACK * st;
GC_LIST * lst;
	for ( i = 0 ; i < GC_THREAD_HASH_SIZE ; i ++ )
		for ( t = gc_thread_hash[i] ; t ; t = t->next )
			for ( st = t->stack ; st ; st = st->parent )
				for ( j = 0 ; j < GC_LIST_HASH_SIZE ; j ++ )
					for ( lst = st->hash[j] ; lst;
							lst = lst->next ) {
						if ( lst->mem != old )
							continue;
						lst->mem = new;
					}
}
*/
