/**********************************************************************
 
	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	"memory_debug.h"
#include	"task.h"
#include	"utils.h"
#include	"lock_level.h"
#include	"blacklist.h"


SEM blacklist_lock;
BLACKLIST *	blacklist_head;

void bl_task();
int bl_flag;

void
init_blacklist()
{
if ( bl_flag )
er_panic("init_blacklist");
bl_flag = 1;
	blacklist_lock = new_lock(LL_BL);
	create_task(bl_task,0,1);
}


int
name_cmp(void * n1,int len1,void * n2,int len2)
{
	if ( len1 != len2 )
		return -1;
	if ( memcmp(n1,n2,len1) == 0 )
		return 0;
	return -1;
}

BLACKLIST *
_search_bl(void * name,int name_len)
{
BLACKLIST *ret;
int i;
	for ( ret = blacklist_head ; ret ; ret = ret->next )
		if ( name_cmp(name,name_len,ret->name,ret->name_len) == 0 )
			return ret;
	ret = d_alloc(sizeof(*ret));
	ret->name = d_alloc(name_len);
	memcpy(ret->name,name,name_len);
	ret->name_len = name_len;
	for ( i = 0 ; i < BLT_MAX ; i ++ ) {
		ret->state[i].state = BLS_OK;
		ret->state[i].func = 0;
	}
	ret->polling_time = get_xltime();
	ret->polling_interval = 0;
	ret->polling_initial = 0;

	ret->next = blacklist_head;
	blacklist_head = ret;
	return ret;
}

#define BLT_SIZE	4
int bl_trace[BLT_SIZE];
int bl_trace_ptr;

void
set_bl_trace(int a)
{
	bl_trace[bl_trace_ptr] = a;
	bl_trace_ptr = (bl_trace_ptr + 1) % 4;
}


BLACKLIST **
scan_bl(BLACKLIST ** blp)
{
BLACKLIST * bl;
int i;
int f;
int ret;


	bl = *blp;
	f = 0;
	for ( i = 0 ; i < BLT_MAX ; i ++ )
		if ( bl->state[i].state != BLS_OK )
			f = 1;
	if ( f == 0 ) {
		*blp = bl->next;
		d_f_ree(bl->name);
		d_f_ree(bl);
set_bl_trace(1);
		return blp;
	}
	for ( i = 0 ; i < BLT_MAX ; i ++ ) {
		if ( bl->state[i].state == BLS_ERROR )
			break;
	}
	if ( i == BLT_MAX )
{set_bl_trace(2);
		return &bl->next;
}
	if ( bl->polling_time > get_xltime() )
{set_bl_trace(3);
		return &bl->next;
}

	unlock_task(blacklist_lock,"scan_bl");
	ret = (*bl->state[i].func)(bl,(void*)0);

	lock_task(blacklist_lock);
	bl->state[i].state = ret;
	bl->polling_time = get_xltime() + bl->polling_interval;
	if ( bl->polling_interval == 0 )
		bl->polling_interval = 1;
	else	bl->polling_interval *= 2;
	if ( bl->polling_interval > BL_INTERVAL_MAX ) {
		bl->state[i].state = BLS_OK;
set_bl_trace(4);
		return &bl->next;
	}
set_bl_trace(5);
	return &bl->next;
}

void
bl_task()
{
BLACKLIST ** blp;
	for ( ; ; ) {
		lock_task(blacklist_lock);
set_bl_trace(0);
		blp = &blacklist_head;
		for ( ; ; ) {
			if ( *blp == 0 )
				break;

			blp = scan_bl(blp);

		}
		unlock_task(blacklist_lock,"bl_task");
		sleep_sec(1);
	}
}




int
bl_do(void * cmd)
{
BLACKLIST * bl;
int ret;
BL_STATE * s;
BL_CMD * _cmd;
unsigned int t;

	_cmd = (BL_CMD*)cmd;
retry:

	lock_task(blacklist_lock);

	bl = _search_bl(_cmd->name,_cmd->name_len);

	s = &bl->state[_cmd->type];

	s->func = _cmd->retry;
	switch ( s->state ) {
	case BLS_OK:
		s->state = BLS_PROCESSING;
		unlock_task(blacklist_lock,"bl_do");

		ret = (*_cmd->proc)(bl,cmd);

		lock_task(blacklist_lock);
		s->state = ret;
		if ( ret == BLS_ERROR ) {
			bl->polling_interval = bl->polling_initial;
			bl->polling_time = get_xltime() + bl->polling_initial;
		}
		wakeup_task((int)s);
		break;
	case BLS_PROCESSING:
		sleep_task((int)s,blacklist_lock);
		goto retry;
	case BLS_ERROR:
/*
ss_printf("BLS_ERROR %i\n",get_tid());
*/
		ret = BLS_ERROR;
		bl->polling_interval = bl->polling_initial;
		t = get_xltime() + bl->polling_interval;
		if ( bl->polling_time > t )
			bl->polling_time = t;
		break;
	default:
		er_panic("bl_do(1)");
	}

	unlock_task(blacklist_lock,"bl_do");
	return ret;
}



