/*
 * time out program copyright (C) 2009 - 2013 H.Niwa
 */

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.

 * 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.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <sys/time.h>

#ifndef __MINGW32__
#include <sys/resource.h>
#endif

#include <string.h>

#include "syserr.h"
#include "timeout.h"

extern int TraceFlag;

extern void PrintContext();

void PrintTimeOutQue();

timeout*	TimeOutLink = NULL;

long long GetTime()
{
	struct timeval tv;
	gettimeofday(&tv, 0);
	
	long long v = tv.tv_sec;
	v *= 1000000LL;
	v += tv.tv_usec;
	return v;
}


timeout::timeout(long long t)
{
	time = t;
	next = TimeOutLink;
	TimeOutLink = this;
}

timeout::~timeout()
{
	timeout* t;
	if (TimeOutLink == this) {
		TimeOutLink = TimeOutLink->next;
		return;
	}
	
	for (t = TimeOutLink; t != NULL; t = t->next) {
		if (t->next == this) {
			t->next = next;
		}
	}
}

char* StackBaseP = 0;

void StackBase()
{
	char	st;
	StackBaseP = &st;
}

int CheckStack()
{
	char	st;
	char*	stp = &st;
	long	dif = StackBaseP - stp;
#ifdef __MINGW32__
	return 0;
#else	
	struct rlimit rl;
	
	if (getrlimit(RLIMIT_STACK, &rl)) {
		return 0;
	}

//printf("CheckStack adr %x %x \n", StackBaseP, stp);
//printf("CheckStack %ld %ld \n", dif, rl.rlim_cur);

	if (dif  > rl.rlim_cur-4096L*4L) {
		return 1;
	}
	return 0;	
#endif
}


void CheckTime()
{
	timeout*	p;
	timeout*	prevp;
	long long	t = GetTime();

//PrintContext();
	
	if (TimeOutLink == NULL) {
		if (CheckStack()) {
			syserr("stack overflow \n");
		}
		return;
	}

	if (CheckStack()) {
		jmp_buf jb;
		memcpy(&jb, &(TimeOutLink->jbuf), sizeof(jmp_buf));

		TimeOutLink = TimeOutLink->next;
		longjmp(jb, -1);
	}
	
	if (t >= TimeOutLink->time) {
		jmp_buf jb;

		if (TraceFlag) {
			printf("*** timeout ***\n");
		}
		memcpy(&jb, &(TimeOutLink->jbuf), sizeof(jmp_buf));

		TimeOutLink = TimeOutLink->next;
		longjmp(jb, -1);
	}

	p = TimeOutLink->next;
	prevp = TimeOutLink;
	for ( ; p != NULL; p = p->next) {
		if (t >= p->time) {
			jmp_buf jb;

			timeout* p2;

			if (TraceFlag) {
				printf("*** timeout ***\n");
			}

			for (p2 = TimeOutLink; p2 != p; p2 = p2->next) {
				delete p2;
			}
			
			memcpy(&jb, &(p->jbuf), sizeof(jmp_buf));

			TimeOutLink = p->next;
			
			longjmp(jb, 1);
		}
		prevp = p;	
	}
	return;
}

timeout* RegistTime(long long tm)
{
	long long t = tm;

	t += GetTime();
	
	timeout* to = new timeout(t);
	
	return to;
}


void PrintTimeOutQue()
{
	timeout* t = TimeOutLink;

	for ( ; t != NULL; t = t->next) {
		printf("--\n %lld\n", t->time);
	}

	printf("--\n\n");
}


