/*
 *
 *   Copyright (c) International Business Machines  Corp., 2001
 *
 *   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 of the License, 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
 */

/*
 * FILE        : openfile.c
 * DESCRIPTION : Create files and open simultaneously
 * HISTORY:
 *   03/21/2001 Paul Larson (plars@us.ibm.com)
 *     -Ported
 *   11/01/2001 Mnaoj Iyer (manjo@austin.ibm.com)
 *     - Modified.
 *	 added #inclide <unistd.h>
 *
 */

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>

#include "test.h"

char *TCID = "openfile01";	/* Test program identifier.    */
int TST_TOTAL = 1;

#define MAXFILES        32768
#define MAXTHREADS      10

/* Control Structure */
struct cb {
	pthread_mutex_t m;
	pthread_cond_t init_cv;
	pthread_cond_t thr_cv;
	int thr_sleeping;
} c;

/* Global Variables */
int numthreads = 10, numfiles = 10;
int debug = 0;
char *filename = "FILETOOPEN";

void setup(void)
{
	tst_tmpdir();
}

void cleanup(void)
{
	tst_rmdir();

}

/* Procedures */
void *threads(void *thread_id);

/* **************************************************************************
 *                              MAIN PROCEDURE                            *
 ************************************************************************** */

int main(int argc, char *argv[])
{
	int i, opt, badopts = 0;
	FILE *fd;
	pthread_t th_id;
	char msg[80] = "";
	extern char *optarg;

	while ((opt = getopt(argc, argv, "df:t:h")) != EOF) {
		switch ((char)opt) {
		case 'd':
			debug = 1;
			break;
		case 'f':
			numfiles = atoi(optarg);
			if (numfiles <= 0)
				badopts = 1;
			break;
		case 't':
			numthreads = atoi(optarg);
			if (numthreads <= 0)
				badopts = 1;
			break;
		case 'h':
		default:
			printf("Usage: openfile [-d] -f FILES -t THREADS\n");
			_exit(1);
		}
	}
	if (badopts) {
		printf("Usage: openfile [-d] -f FILES -t THREADS\n");
		_exit(1);
	}

	setup();

	/* Check if numthreads is less than MAXFILES */
	if (numfiles > MAXFILES) {
		sprintf(msg, "%s\nCannot use %d files", msg, numfiles);
		sprintf(msg, "%s, used %d files instead\n", msg, MAXFILES);
		numfiles = MAXFILES;
	}

	/* Check if numthreads is less than MAXTHREADS */
	if (numthreads > MAXTHREADS) {
		sprintf(msg, "%s\nCannot use %d threads", msg, numthreads);
		sprintf(msg, "%s, used %d threads instead\n", msg, MAXTHREADS);
		numthreads = MAXTHREADS;
	}

	/* Create files */
	if ((fd = fopen(filename, "w")) == NULL) {
		tst_resm(TFAIL, "Could not create file");
		cleanup();
	}

	/* Initialize thread control variables, lock & condition */
	pthread_mutex_init(&c.m, NULL);
	pthread_cond_init(&c.init_cv, NULL);
	pthread_cond_init(&c.thr_cv, NULL);
	c.thr_sleeping = 0;

	/* Grab mutex lock */
	if (pthread_mutex_lock(&c.m)) {
		tst_resm(TFAIL, "failed to grab mutex lock");
		fclose(fd);
		unlink(filename);
		cleanup();
	}

	printf("Creating Reading Threads\n");

	/* Create threads */
	for (i = 0; i < numthreads; i++)
		if (pthread_create(&th_id, NULL, threads,
				   (void *)(uintptr_t) i)) {
			tst_resm(TFAIL,
				 "failed creating a pthread; increase limits");
			fclose(fd);
			unlink(filename);
			cleanup();
		}

	/* Sleep until all threads are created */
	while (c.thr_sleeping != numthreads)
		if (pthread_cond_wait(&c.init_cv, &c.m)) {
			tst_resm(TFAIL,
				 "error while waiting for reading threads");
			fclose(fd);
			unlink(filename);
			cleanup();
		}

	/* Wake up all threads */
	if (pthread_cond_broadcast(&c.thr_cv)) {
		tst_resm(TFAIL, "failed trying to wake up reading threads");
		fclose(fd);
		unlink(filename);
		cleanup();
	}
	/* Release mutex lock */
	if (pthread_mutex_unlock(&c.m)) {
		tst_resm(TFAIL, "failed to release mutex lock");
		fclose(fd);
		unlink(filename);
		cleanup();
	}

	tst_resm(TPASS, "Threads are done reading");

	fclose(fd);
	unlink(filename);
	cleanup();
	_exit(0);
}

/* **************************************************************************
 *				OTHER PROCEDURES			    *
 ************************************************************************** */

void close_files(FILE * fd_list[], int len)
{
	int i;
	for (i = 0; i < len; i++) {
		fclose(fd_list[i]);
	}
}

/* threads: Each thread opens the files specified */
void *threads(void *thread_id_)
{
	int thread_id = (uintptr_t) thread_id_;
	char errmsg[80];
	FILE *fd_list[MAXFILES];
	int i;

	/* Open files */
	for (i = 0; i < numfiles; i++) {
		if (debug)
			printf("Thread  %d : Opening file number %d \n",
			       thread_id, i);
		if ((fd_list[i] = fopen(filename, "rw")) == NULL) {
			sprintf(errmsg, "FAIL - Couldn't open file #%d", i);
			perror(errmsg);
			if (i > 0) {
				close_files(fd_list, i - 1);
			}
			unlink(filename);
			pthread_exit((void *)1);
		}
	}

	/* Grab mutex lock */
	if (pthread_mutex_lock(&c.m)) {
		perror("FAIL - failed to grab mutex lock");
		close_files(fd_list, numfiles);
		unlink(filename);
		pthread_exit((void *)1);
	}

	/* Check if you should wake up main thread */
	if (++c.thr_sleeping == numthreads)
		if (pthread_cond_signal(&c.init_cv)) {
			perror("FAIL - failed to signal main thread");
			close_files(fd_list, numfiles);
			unlink(filename);
			pthread_exit((void *)1);
		}

	/* Sleep until woken up */
	if (pthread_cond_wait(&c.thr_cv, &c.m)) {
		perror("FAIL - failed to wake up correctly");
		close_files(fd_list, numfiles);
		unlink(filename);
		pthread_exit((void *)1);
	}

	/* Release mutex lock */
	if (pthread_mutex_unlock(&c.m)) {
		perror("FAIL - failed to release mutex lock");
		close_files(fd_list, numfiles);
		unlink(filename);
		pthread_exit((void *)1);
	}

	/* Close file handles and exit */
	close_files(fd_list, numfiles);
	unlink(filename);
	pthread_exit(NULL);
}