/*
 * lat_select.c - time select system call
 *
 * usage: lat_select [-P <parallelism>] [-W <warmup>] [-N <repetitions>] [n]
 *
 * Copyright (c) 1996 Larry McVoy.  Distributed under the FSF GPL with
 * additional restriction that results may published only if
 * (1) the benchmark is unmodified, and
 * (2) the version in the sccsid below is included in the report.
 */
char	*id = "$Id$\n";

#include "bench.h"

void initialize(iter_t iterations, void *cookie);
void cleanup(iter_t iterations, void *cookie);
void doit(iter_t iterations, void *cookie);
void writer(int w, int r);
void server(void* cookie);

typedef int (*open_f)(void* cookie);
int  open_file(void* cookie);
int  open_socket(void* cookie);

typedef struct _state {
	char	fname[L_tmpnam];
	open_f	fid_f;
	pid_t	pid;
	int	sock;
	int	fid;
	int	num;
	int	max;
	fd_set  set;
} state_t;

int
main(int ac, char **av)
{
	state_t state;
	int parallel = 1;
	int warmup = 0;
	int repetitions = -1;
	int c;
	char* usage = "[-n <#descriptors>] [-P <parallelism>] [-W <warmup>] [-N <repetitions>] file|tcp\n";
	char	buf[256];

	morefds();  /* bump fd_cur to fd_max */
	state.num = 200;
	while (( c = getopt(ac, av, "P:W:N:n:")) != EOF) {
		switch(c) {
		case 'P':
			parallel = atoi(optarg);
			if (parallel <= 0) lmbench_usage(ac, av, usage);
			break;
		case 'W':
			warmup = atoi(optarg);
			break;
		case 'N':
			repetitions = atoi(optarg);
			break;
		case 'n':
			state.num = bytes(optarg);
			break;
		default:
			lmbench_usage(ac, av, usage);
			break;
		}
	}

	if (optind + 1 != ac) {
		lmbench_usage(ac, av, usage);
	}

	if (streq("tcp", av[optind])) {
		state.fid_f = open_socket;
		server(&state);
		benchmp(initialize, doit, cleanup, 0, parallel, 
			warmup, repetitions, &state);
		sprintf(buf, "Select on %d tcp fd's", state.num);
		kill(state.pid, SIGKILL);
		waitpid(state.pid, NULL, 0);
		micro(buf, get_n());
	} else if (streq("file", av[optind])) {
		state.fid_f = open_file;
		server(&state);
		benchmp(initialize, doit, cleanup, 0, parallel, 
			warmup, repetitions, &state);
		unlink(state.fname);
		sprintf(buf, "Select on %d fd's", state.num);
		micro(buf, get_n());
	} else {
		lmbench_usage(ac, av, usage);
	}

	exit(0);
}

void
server(void* cookie)
{
	int pid;
	state_t* state = (state_t*)cookie;

	pid = getpid();
	state->pid = 0;

	if (state->fid_f == open_file) {
		/* Create a temporary file for clients to open */
		sprintf(state->fname, "lat_selectXXXXXX");
		state->fid = mkstemp(state->fname);
		if (state->fid <= 0) {
			char buf[L_tmpnam+128];
			sprintf(buf, "lat_select: Could not create temp file %s", state->fname);
			perror(buf);
			exit(1);
		}
		close(state->fid);
		return;
	}

	/* Create a socket for clients to connect to */
	state->sock = tcp_server(TCP_SELECT, SOCKOPT_REUSE);
	if (state->sock <= 0) {
		perror("lat_select: Could not open tcp server socket");
		exit(1);
	}

	/* Start a server process to accept client connections */
	switch(state->pid = fork()) {
	case 0:
		/* child server process */
		while (pid == getppid()) {
			int newsock = tcp_accept(state->sock, SOCKOPT_NONE);
			read(newsock, &state->fid, 1);
			close(newsock);
		}
		exit(0);
	case -1:
		/* error */
		perror("lat_select::server(): fork() failed");
		exit(1);
	default:
		break;
	}
}

int
open_socket(void* cookie)
{
	return tcp_connect("localhost", TCP_SELECT, SOCKOPT_NONE);
}

int
open_file(void* cookie)
{
	state_t* state = (state_t*)cookie;

	return open(state->fname, O_RDONLY);
}

void
doit(iter_t iterations, void * cookie)
{
	state_t * 	state = (state_t *)cookie;
	fd_set		nosave;
	static struct timeval tv;
	
	tv.tv_sec = 0;
	tv.tv_usec = 0;

	while (iterations-- > 0) {
		nosave = state->set;
		select(state->num, 0, &nosave, 0, &tv);
	}
}

void
initialize(iter_t iterations, void *cookie)
{
	state_t * state = (state_t *)cookie;
	int	n;
	int	N = state->num, fid, fd;

	if (iterations) return;

	fid = (*state->fid_f)(cookie);
	if (fid <= 0) {
		perror("Could not open device");
		exit(1);
	}
	state->max = 0;
	FD_ZERO(&(state->set));
	for (n = 0; n < N; n++) {
		fd = dup(fid);
		if (fd == -1) break;
		if (fd > state->max)
			state->max = fd;
		FD_SET(fd, &(state->set));
	}
	state->max++;
	close(fid);
	if (n != N)
		exit(1);
}

void
cleanup(iter_t iterations, void *cookie)
{
	int	i;
	state_t * state = (state_t *)cookie;

	if (iterations) return;

	for (i = 0; i <= state->max; ++i) {
		if (FD_ISSET(i, &(state->set)))
			close(i);
	}
	FD_ZERO(&(state->set));
}

	     
