/**********************************************************************
 
	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	<stdio.h>
#include	"task.h"
#include	"lock_level.h"
#include	"log.h"
#include	"memory_routine.h"
#include	"xl.h"
#include	"stream.h"

int gc_log_trace(AVT_NODE * a,MS_NODE **);
void gc_log(MEM_STATISTICS * ms);
void gc_tick_notin_tick();
void __gc_tick(int non_tick_flag);

#define GC_TICK_INTERVAL	10
#define GC_THRESHOLD_UP		1000000
#define GC_THRESHOLD_DOWN	 500000

#define GC_MS_INTERVAL		120

extern int malloc_count;
int free_count;
SEM gc_task_lock;
SEM notin_lock;

int gc_tick_interval;


int ms_time;

int
gc_log_trace(AVT_NODE * a,MS_NODE ** ms_n)
{
MS_NODE * n;
	n = a->data;
	if ( *ms_n == 0 )
		*ms_n = n;
	else {
		if ( (*ms_n)->total_size < n->total_size )
			*ms_n = n;
	}
	return 0;
}

MS_NODE ms_max_info;

void
gc_log(MEM_STATISTICS * ms)
{
MS_NODE * max;
	max = 0;
	avt_trace_from_large(ms->root,gc_log_trace,&max);
	ms_max_info = *max;
}

void
__gc_tick(int non_tick_flag)
{
int ret;
int res;
unsigned int st_time,interval;
MEM_STATISTICS ms;
MEM_STATISTICS * msp;
int now;

//log_printf(LOG_DEBUG,LOG_LAYER_XL,0,"GC START\n");

	now = get_xltime();
	if ( now - ms_time > GC_MS_INTERVAL ) {
		msp = &ms;
		ms_time = now;
	}
	else	msp = 0;

	res = s_check_resource();

	st_time = get_xltime();

	lock_task(gc_task_lock);

	gc_lock();

	lock_mem();

	GC_POSITION;

	gc();



	GC_POSITION;

	sp_gc();

	GC_POSITION;

	free_count = ret = _mem_gc(msp);

	after_gc();

	unlock_mem();

	gc_unlock();

	if ( msp ) {
		gc_log(&ms);
		free_mem_statistics(&ms);
	}

	unlock_task(gc_task_lock,"gc_task");

	interval = get_xltime() - st_time;

	if ( free_count > GC_THRESHOLD_UP || res < 0 ) {
		gc_tick_interval = gc_tick_interval/2;
		if ( gc_tick_interval < 1)
			gc_tick_interval = 1;
		if ( gc_tick_interval < 5*interval )
			gc_tick_interval = 5*interval;
		if ( non_tick_flag == 0 )
			change_tick(gc_tick_interval);
	}
	else if ( free_count < GC_THRESHOLD_DOWN ) {
		gc_tick_interval ++;
		if ( gc_tick_interval > GC_TICK_INTERVAL )
			gc_tick_interval = GC_TICK_INTERVAL;
		if ( gc_tick_interval < 5*interval )
			gc_tick_interval = 5*interval;
		if ( non_tick_flag == 0 )
			change_tick(gc_tick_interval);
	}
	else if ( gc_tick_interval < 5*interval ) {
		gc_tick_interval = 5*interval;
		if ( non_tick_flag == 0 )
			change_tick(gc_tick_interval);
	}


//	log_printf(LOG_DEBUG,LOG_LAYER_XL,0,"GC END %i %i\n",ret,malloc_count);

}

void
gc_tick()
{
	__gc_tick(0);
}

void
gc_tick_notin_tick(char * msg,int no)
{
	if ( gc_task_lock.so == 0 )
		return;

//lock_task(notin_lock);
	if ( msg )
		printf("TICK %s::%i\n",msg,no);
	__gc_tick(1);
	
	if ( msg )
		printf("TICK %s::%i END\n",msg,no);
//unlock_task(notin_lock,"tick");

}

void
gc_enable()
{

	gc_lock();

	lock_mem();
/*
printf("GC START\n");
*/
	gc();
	sp_gc();
	_mem_gc(0);
	after_gc();
/*
printf("END %i %i\n",ret,malloc_count);
*/
	unlock_mem();

	gc_unlock();
}

void
init_gc_task()
{

	gc_task_lock = new_lock(LL_GC_TASK);
	notin_lock = new_lock(LL_GC_NOTIN);
	gc_tick_interval = GC_TICK_INTERVAL;
	new_tick((void(*)())gc_tick,GC_TICK_INTERVAL,0);
}
