/*
 * Copyright 2006, Steven Rostedt
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>
#include <pthread.h>
#include <signal.h>
#include <time.h>
#include <sys/time.h>

#define SLEEP_TIME 30
#define THREADS 5

int run = 1;
int print;
int printing;
pid_t mainpid;

#ifdef __i386__
#  define rdtscll(val) \
     __asm__ __volatile__("rdtsc" : "=A" (val))
#elif defined(__x86_64__)
# define rdtscll(val) do { \
     unsigned int __a,__d; \
     asm volatile("rdtsc" : "=a" (__a), "=d" (__d)); \
     (val) = ((unsigned long)__a) | (((unsigned long)__d)<<32); \
} while(0)
#else
# warning cant test rdtscll
# define rdtscll(x) do { x = 0; } while(0)
#endif

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


void sig(int sig)
{
	return;
}

int get_timer(unsigned long *secs, unsigned long *nsecs)
{
	int ret;
	struct timespec tp;

	if ((ret = clock_gettime(CLOCK_MONOTONIC, &tp)))
		perror("gettime");

	*secs = tp.tv_sec;
	*nsecs = tp.tv_nsec;

	return ret;
}

static int test_time(unsigned long secs, unsigned long nsecs)
{
	static unsigned long last_secs;
	static unsigned long last_nsecs;
	static unsigned long long last_tsc;
	unsigned long long tsc;
	int ret = 0;
	static int once;

	rdtscll(tsc);
	if (last_tsc > tsc && !(once++))
		printf("last tsc is %lld  this tsc is %lld\n",
		       last_tsc, tsc);
	last_tsc = tsc;

	if ((last_secs > secs) ||
	    (last_secs == secs && last_nsecs > nsecs))
		ret = 1;
	if (ret)
		printf("Failed! prev: %ld.%09ld   current: %ld.%09ld\n",
		       last_secs, last_nsecs,
		       secs, nsecs);
	last_secs = secs;
	last_nsecs = nsecs;

	return ret;
}

struct func_info {
	int id;
	int threads;
};

void *func(void *dat)
{
	struct func_info *info = dat;
	int id = info->id;
	unsigned long secs, nsecs;

	printf("hello from thread %d!\n",id);
	while (run) {
		pthread_mutex_lock(&mutex);
		get_timer(&secs, &nsecs);
		if (test_time(secs, nsecs)) {
			run = 0;
			kill(mainpid, SIGALRM);
		}
		pthread_mutex_unlock(&mutex);
	}
	return NULL;
}

void run_test(int secs, int threads)
{
	pthread_t t[threads];
	struct func_info info[threads];
	int i;
	for (i=0; i < threads; i++) {
		info[i].id = i;
		info[i].threads = threads;
		if (pthread_create(&t[i],NULL,func,&info[i])) {
			perror("pthread_creat");
			exit(-1);
		}
	}
	
	if (secs > 0) {
		if (run)
			sleep(secs);
	} else
		for (;run;)
			sleep(100);
	
	run = 0;
	for (i=0; i < threads; i++) {
		 pthread_join(t[i],NULL);
	}
}

void usage(char **argv)
{
	char *p;
	for (p = argv[0]+strlen(argv[0]); p >= argv[0] && *p != '/'; p--)
		;
	printf("\nusage: %s [-pPh] [-s seconds] [-t threads]\n",p);
	printf(
	       "   -s  second to run test\n"
	       "   -t threads to run\n"
	       "\n");
	exit(-1);
}

int main (int argc, char **argv)
{
	int c;
	int secs = SLEEP_TIME;
	int threads = THREADS;

	mainpid = getpid();
	signal(SIGALRM, sig);

	while ((c=getopt(argc, argv, "hPpt:s:")) >= 0) {
		switch (c) {
			case 's':
				secs = atoi(optarg);
				break;
			case 't':
				threads = atoi(optarg);
				break;
			case 'h':
			default:
				usage(argv);
		}
	}
	
	printf("main program pid=%d\n", getpid());
	
	run_test(secs, threads);
	
	exit(0);
	
	return 0;
}
