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


#define SW_HASH_SIZE	99

D_SEM sw_lock;


SW_HASH * hash[SW_HASH_SIZE];


void
sw_init()
{
	sw_lock = xx_new_lock(LL_SLEEP_WAKEUP,__FILE__,__LINE__);
}

int intr_cnt;
int again_cnt;

void
t_sleep_task(unsigned int key,D_SEM s,int pri_flag)
{
unsigned int kk;
SW_HASH * h, **hp;
int pri;
SW_HASH_TID ttt, ** tp;
	kk = key%SW_HASH_SIZE;
	if ( pri_flag )
		pri = push_pri(PRI_SLEEP_WAIT);
	_lock_task(sw_lock,__FILE__,__LINE__);
	for ( h = hash[kk]; h ; h = h->next )
		if ( h->key == key ) {
			h->cnt ++;
			_unlock_task(sw_lock,"sleep_task(1)",__FILE__,__LINE__);
			goto last;
		}

	ttt.tid = _get_tid();

	h = new_block();
	h->key = key;
	h->cnt = 1;
	ttt.next = h->tid_list;
	h->tid_list = &ttt;
	h->first_sleep_time = get_xltime();
	h->last_wakeup_time = 0;
	sem_init(&h->sem,0,1);
	sem_trywait(&h->sem);
	h->next = hash[kk];
	hash[kk] = h;
	_unlock_task(sw_lock,"sleep_task(3)",__FILE__,__LINE__);
last:
	if ( s )
		_unlock_task(s,"sleep_task(4)",__FILE__,__LINE__);
retry1:
	errno = 0;
	if ( sem_wait(&h->sem) ) {
		switch ( errno ) {
		case EINTR:
			intr_cnt ++;
			goto retry1;
		case EAGAIN:
			again_cnt ++;
			sleep_sec(1);
			goto retry1;
		default:
			er_panic("sem_wait");
		}
	}
	_lock_task(sw_lock,__FILE__,__LINE__);
	for ( tp = &h->tid_list ; *tp ; tp = &(*tp)->next )
		if ( *tp == &ttt ) {
			*tp = ttt.next;
			break;
		}
	h->cnt --;
	if ( h->cnt == 0 ) {
		for ( hp = &hash[kk] ; *hp ; hp = &(*hp)->next )
			if ( (*hp)->key == key ) {
				*hp = (*hp)->next;
				break;
			}

		sem_destroy(&h->sem);
		free_block(h);
	}
	_unlock_task(sw_lock,"sleep_task(5)",__FILE__,__LINE__);
	if ( pri_flag )
		change_pri(0,pri);
	return;
}


void
t_wakeup_task(unsigned int key,int pri_flag)
{
unsigned int kk;
int i;
SW_HASH * h, ** hp;
int pri;

	kk = key%SW_HASH_SIZE;
	if ( pri_flag )
		pri = push_pri(PRI_SLEEP_WAIT);
	_lock_task(sw_lock,__FILE__,__LINE__);
	for ( hp = &hash[kk] ; *hp ; hp = &(*hp)->next )
		if ( (*hp)->key == key )
			goto next;
	_unlock_task(sw_lock,"wakeup_task(1)",__FILE__,__LINE__);
	if ( pri_flag )
		change_pri(0,pri);
	return;
next:
	h = *hp;
//	*hp = h->next;
	for ( i = h->cnt ; i ; i -- )
		for ( ; sem_post(&h->sem); );
	h->last_wakeup_time = get_xltime();
	_unlock_task(sw_lock,"wakeup_task(2)",__FILE__,__LINE__);
	if ( pri_flag )
		change_pri(0,pri);
}

void
_sleep_task(unsigned int key,D_SEM s,char* file,int line)
{
	t_sleep_task(key,s,1);
}

void
_wakeup_task(unsigned int key,char * file,int line)
{
	t_wakeup_task(key,1);
}


void
send_sw_trigger(unsigned int key)
{
unsigned int kk;
SW_HASH * h, ** hp;
SW_HASH_TID * tp;

	kk = key%SW_HASH_SIZE;
	_lock_task(sw_lock,__FILE__,__LINE__);
	for ( hp = &hash[kk] ; *hp ; hp = &(*hp)->next )
		if ( (*hp)->key == key )
			goto next;
	_unlock_task(sw_lock,"wakeup_task(1)",__FILE__,__LINE__);
	return;
next:
	h = *hp;
	for ( tp = h->tid_list ; tp ; tp = tp->next )
		pthread_kill((pthread_t)tp->tid.tid, SIGALRM);
	_unlock_task(sw_lock,"wakeup_task(2)",__FILE__,__LINE__);
}
