/*
 *
 *   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        : pth_str02.c
 *  DESCRIPTION : Create n threads
 *  HISTORY:
 *    05/16/2001 Paul Larson (plars@us.ibm.com)
 *      -Ported
 *
 */

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include "test.h"

/* Defines
 *
 * DEFAULT_NUM_THREADS: Default number of threads to create,
 * user can specifiy with [-n] command line option.
 *
 * USAGE: usage statement
 */

#define DEFAULT_NUM_THREADS 		10
#define USAGE	"\nUsage: %s [-l | -n num_threads] [-d]\n\n" \
		"\t-l		     Test as many as threads as possible\n" \
		"\t-n num_threads    Number of threads to create\n" \
		"\t-d                Debug option\n\n"

/*
 * Function prototypes
 *
 * sys_error (): System error message function
 * error (): Error message function
 * parse_args (): Parses command line arguments
 */

static void sys_error(const char *, int);
static void error(const char *, int);
static void parse_args(int, char **);
void *thread(void *);

/*
 * Global Variables
 */

int num_threads = DEFAULT_NUM_THREADS;
int test_limit = 0;
int debug = 0;

char *TCID = "pth_str02";
int TST_TOTAL = 1;

/*---------------------------------------------------------------------+
|                               main ()                                |
| ==================================================================== |
|                                                                      |
| Function:  Main program  (see prolog for more details)               |
|                                                                      |
+---------------------------------------------------------------------*/
int main(int argc, char **argv)
{
	/*
	 * Parse command line arguments and print out program header
	 */
	parse_args(argc, argv);

	if (test_limit) {
		tst_resm(TINFO, "Creating as many threads as possible");
	} else {
		tst_resm(TINFO, "Creating %d threads", num_threads);
	}
	thread(0);

	/*
	 * Program completed successfully...
	 */
	tst_resm(TPASS, "Test passed");
	exit(0);
}

/*---------------------------------------------------------------------+
|                               thread ()                              |
| ==================================================================== |
|                                                                      |
| Function:  Recursively creates threads while num < num_threads       |
|                                                                      |
+---------------------------------------------------------------------*/
void *thread(void *parm)
{
	intptr_t num = (intptr_t) parm;
	pthread_t th;
	pthread_attr_t attr;
	size_t stacksize = 1046528;
	int pcrterr;

	/*
	 * Create threads while num < num_threads...
	 */
	if (test_limit || (num < num_threads)) {

		if (pthread_attr_init(&attr))
			sys_error("pthread_attr_init failed", __LINE__);
		if (pthread_attr_setstacksize(&attr, stacksize))
			sys_error("pthread_attr_setstacksize failed", __LINE__);
		if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE))
			sys_error("pthread_attr_setdetachstate failed",
				  __LINE__);
		/************************************************/
		/*   pthread_create does not touch errno.  It RETURNS the error
		 *   if it fails.  errno has no bearing on this test, so it was
		 *   removed and replaced with return value check(see man page
		 *   for pthread_create();
		 */
		pcrterr = pthread_create(&th, &attr, thread, (void *)(num + 1));
		if (pcrterr != 0) {
			if (test_limit) {
				tst_resm(TINFO,
					 "Testing pthread limit, %d pthreads created.",
					 (int)num);
				pthread_exit(0);
			}
			if (pcrterr == EAGAIN) {
				tst_resm(TINFO,
					 "Thread [%d]: unable to create more threads!",
					 (int)num);
				return NULL;
			} else
				sys_error("pthread_create failed", __LINE__);
		}
		pthread_join(th, (void *)NULL);
	}

	return 0;
	/*
	   pthread_exit(0);
	 */
}

/*---------------------------------------------------------------------+
|                             parse_args ()                            |
| ==================================================================== |
|                                                                      |
| Function:  Parse the command line arguments & initialize global      |
|            variables.                                                |
|                                                                      |
+---------------------------------------------------------------------*/
static void parse_args(int argc, char **argv)
{
	int i;
	int errflag = 0;
	char *program_name = *argv;

	while ((i = getopt(argc, argv, "dln:?")) != EOF) {
		switch (i) {
		case 'd':	/* debug option */
			debug++;
			break;
		case 'l':	/* test pthread limit */
			test_limit++;
			break;
		case 'n':	/* number of threads */
			num_threads = atoi(optarg);
			break;
		case '?':
			errflag++;
			break;
		}
	}

	/* If any errors exit program */
	if (errflag) {
		fprintf(stderr, USAGE, program_name);
		exit(2);
	}
}

/*---------------------------------------------------------------------+
|                             sys_error ()                             |
| ==================================================================== |
|                                                                      |
| Function:  Creates system error message and calls error ()           |
|                                                                      |
+---------------------------------------------------------------------*/
static void sys_error(const char *msg, int line)
{
	char syserr_msg[256];

	sprintf(syserr_msg, "%s: %s\n", msg, strerror(errno));
	error(syserr_msg, line);
}

/*---------------------------------------------------------------------+
|                               error ()                               |
| ==================================================================== |
|                                                                      |
| Function:  Prints out message and exits...                           |
|                                                                      |
+---------------------------------------------------------------------*/
static void error(const char *msg, int line)
{
	fprintf(stderr, "ERROR [line: %d] %s\n", line, msg);
	tst_resm(TFAIL, "Test failed");
	exit(-1);
}
