| /* |
| * Copyright (C) Bull S.A. 2001 |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| /******************************************************************************/ |
| /* */ |
| /* Dec-03-2001 Created: Jacky Malcles & Jean Noel Cordenner */ |
| /* These tests are adapted from AIX float PVT tests. */ |
| /* */ |
| /******************************************************************************/ |
| #include "tfloat.h" |
| |
| #include "test.h" |
| |
| #define SAFE_FREE(p) { if (p) { free(p); (p)=NULL; } } |
| /* LTP status reporting */ |
| char *TCID; /* Test program identifier. */ |
| int TST_TOTAL=1; /* Total number of test cases. */ |
| |
| /* To avoid extensive modifications to the code, use this bodge */ |
| #define exit(x) myexit(x) |
| |
| void |
| myexit (int x) |
| { |
| if (x) |
| tst_resm (TFAIL, "Test failed"); |
| else |
| tst_resm (TPASS, "Test passed"); |
| tst_exit(); |
| } |
| |
| TH_DATA *pcom; |
| TH_DATA **tabcom; |
| TH_DATA **tabcour; |
| #ifndef PATH_MAX |
| #define PATH_MAX 1024 |
| #endif |
| char datadir[PATH_MAX]; /* DATA directory */ |
| |
| #ifndef PTHREAD_THREADS_MAX |
| #define PTHREAD_THREADS_MAX 1024 |
| #endif |
| #define DEFAULT_NUM_THREADS 20 |
| int num_threads = DEFAULT_NUM_THREADS; |
| int num_loops=500; |
| |
| int sig_cancel = 0; /* flag set by handle_signals to tell initial thread |
| to stop creating new threads (signal caught) */ |
| |
| int indice = 0; /* # of threads created, to be canceled by handle_signals |
| or waited for by initial thread */ |
| |
| pthread_mutex_t sig_mutex; |
| pthread_t *threads; |
| |
| int debug = 0; |
| int true = 1; |
| |
| static void *handle_signals (void *); |
| |
| static void sys_error(const char *, int); |
| |
| const double EPS= 0.1e-300; |
| |
| const int nb_func = NB_FUNC; |
| |
| int generate(char *datadir, char *bin_path) |
| { |
| char *cmdline; |
| char *fmt = "cd %s; %s/%s %s"; |
| |
| cmdline = malloc (2 * strlen(bin_path) + strlen(datadir) + |
| strlen(GENERATOR) + strlen(fmt)); |
| if (cmdline == NULL) |
| return(1); |
| sprintf(cmdline,fmt,datadir,bin_path,GENERATOR,bin_path); |
| system(cmdline); |
| free(cmdline); |
| return(0); |
| } |
| |
| static void |
| cleanup(void) |
| { |
| tst_rmdir(); |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| int opt = 0; |
| pid_t pid; |
| |
| char *bin_path, *ltproot; |
| void *exit_value; |
| pthread_attr_t newattr; |
| pthread_t sig_hand; |
| size_t stacksize = 2093056; |
| int th_num; |
| int retvalend = 0; |
| int retval = 0; |
| int error =0; |
| /*int time=1;*/ |
| int i; |
| |
| /* Generate test ID from invocation name */ |
| if ((TCID = strrchr (argv[0], '/')) != NULL) |
| TCID++; |
| else |
| TCID = argv[0]; |
| ltproot = getenv("LTPROOT"); |
| if (ltproot == NULL || strlen(ltproot) == 0) { |
| tst_brkm(TBROK, NULL, |
| "You must set $LTPROOT before executing this test"); |
| } |
| bin_path = malloc(strlen(ltproot) + 16); |
| if (bin_path == NULL) { |
| tst_brkm(TBROK|TERRNO, NULL, "malloc failed"); |
| } |
| sprintf(bin_path, "%s/testcases/bin", ltproot); |
| |
| tst_tmpdir(); |
| |
| setbuf(stdout, (char *)0); |
| setbuf(stderr, (char *)0); |
| datadir[0] = '.'; |
| datadir[1] = '\0'; |
| |
| if (argc != 1) { |
| while ((opt = getopt( argc, argv, "vn:l:D:?")) != EOF) { |
| switch (opt) { |
| case 'D': |
| strncpy(datadir, optarg, PATH_MAX); |
| break; |
| case 'l': |
| num_loops = atoi(optarg); |
| break; |
| case 'n': |
| num_threads = atoi(optarg); |
| break; |
| case 'v': |
| ++debug; /* verbose mode */ |
| break; |
| default: |
| fprintf(stderr, "usage: %s [-n number_of_threads] [-v]\n", argv[0]); |
| fprintf(stderr, "[-l number_of_loops] "); |
| fprintf(stderr, "[-D DATAs absolute path]\n"); |
| exit(1); |
| } |
| } |
| } |
| switch (pid = fork()) { |
| case -1: |
| tst_brkm(TBROK|TERRNO, cleanup, "fork failed"); |
| case 0: |
| generate(datadir, bin_path); |
| exit(0); |
| default: |
| waitpid(pid, NULL, 0); |
| } |
| SAFE_FREE(bin_path); |
| |
| if (debug) { |
| tst_resm(TINFO, |
| "%s: will run for %d loops; using %s as a data directory", |
| argv[0], num_loops, datadir); |
| } |
| if (num_threads <= 0) { |
| tst_resm(TWARN, |
| "num_threads undefined or incorrect, using \"1\""); |
| num_threads = 1; |
| } |
| |
| if (nb_func * num_threads > PTHREAD_THREADS_MAX-2) |
| while (nb_func * num_threads > PTHREAD_THREADS_MAX-2) |
| num_threads--; |
| if (debug) |
| tst_resm(TINFO, |
| "%s: will run %d functions, %d threads per function", |
| argv[0], nb_func, num_threads); |
| |
| retval = pthread_mutex_init (&sig_mutex, NULL); |
| if (retval != 0) |
| sys_error("main : mutex_init(&sig_mutex) FAILED", __LINE__); |
| |
| retval = pthread_create(&sig_hand, NULL, handle_signals, NULL); |
| if (retval != 0) |
| sys_error("main : create(&sig_hand) FAILED", __LINE__); |
| |
| /* |
| * Start all calculation threads... |
| */ |
| threads = malloc(nb_func * num_threads * sizeof(pthread_t)); |
| if (threads == NULL) |
| tst_brkm(TFAIL|TERRNO, cleanup, "malloc failed"); |
| |
| tabcom = malloc((sizeof(TH_DATA *) * nb_func * num_threads)); |
| if (!tabcom) |
| tst_brkm(TFAIL|TERRNO, cleanup, "malloc failed"); |
| tabcour = tabcom; |
| |
| retval = pthread_attr_init(&newattr); |
| if (retval != 0) |
| sys_error("main : attr_init(&newattr) FAILED", __LINE__); |
| |
| if (pthread_attr_setstacksize (&newattr, stacksize)) |
| sys_error("main: pthread_attr_setstacksize failed", __LINE__); |
| |
| retval = pthread_attr_setdetachstate(&newattr, PTHREAD_CREATE_JOINABLE); |
| if (retval != 0) |
| sys_error("main : attr_setdetachstate(&newattr) FAILED", |
| __LINE__); |
| |
| /* run the nb_func functions on num_threads */ |
| |
| indice = 0; |
| for (i = 0; i < nb_func; i++) { |
| |
| for (th_num = 0; th_num < num_threads; th_num++) { |
| |
| /* allocate struct of commucation with the thread */ |
| pcom = calloc (1, sizeof(TH_DATA)); |
| if (pcom == NULL) |
| tst_brkm(TFAIL|TERRNO, cleanup, "calloc failed"); |
| *tabcour = (TH_DATA *) pcom; |
| tabcour++; |
| /* |
| * update structure of communication |
| */ |
| pcom->th_num = th_num; |
| pcom->th_func = th_func[i]; |
| |
| pthread_mutex_lock(&sig_mutex); |
| |
| if (sig_cancel) { /* stop processing right now! */ |
| pthread_mutex_unlock(&sig_mutex); |
| goto finished; |
| } |
| retval = pthread_create(&threads[indice], &newattr, |
| thread_code, (void *) pcom); |
| if (retval != 0) |
| sys_error("main : create FAILED",__LINE__); |
| indice++; |
| pthread_mutex_unlock(&sig_mutex); |
| |
| }/* num_threads */ |
| } /* for i*/ |
| |
| /*alarm(60*time);*/ /* start all threads for TEST_time */ |
| |
| /* |
| * Wait for the threads finish their task |
| * pthread_join () will block |
| */ |
| |
| finished: |
| if (debug) { |
| tst_resm(TINFO, |
| "initial thread: Waiting for %d threads to finish", |
| indice); |
| } |
| tabcour = tabcom; |
| |
| for (th_num = 0; th_num < indice; th_num++) { |
| retvalend = pthread_join (threads[th_num], &exit_value); |
| if (retvalend != 0) |
| sys_error("finish : join FAILED",__LINE__); |
| |
| /* test the result in TH_DATA : communication buffer */ |
| pcom = * tabcour++; |
| if (pcom->th_result != 0) { |
| error++; |
| tst_resm(TFAIL, |
| "thread %d (%s) terminated unsuccessfully %d " |
| "errors/%d loops\n%s", |
| th_num, pcom->th_func.fident, pcom->th_nerror, |
| pcom->th_nloop, pcom->detail_data); |
| } |
| else if (debug) { |
| tst_resm(TINFO, |
| "thread %d (%s) terminated successfully %d loops", |
| th_num, pcom->th_func.fident, pcom->th_nloop - 1); |
| } |
| SAFE_FREE(pcom); |
| |
| } |
| pthread_cancel(sig_hand); |
| pthread_join(sig_hand, NULL); |
| SAFE_FREE(tabcom); |
| SAFE_FREE(threads); |
| tst_rmdir(); |
| if (error) |
| exit(1); |
| else |
| exit(0); |
| return 0; |
| } |
| |
| /*----------------------------------------------------------------------+ |
| | handle_signals () | |
| | ======================================================================| |
| | | |
| | Function: .... | |
| | If SIGALRM or SIGUSR1 or SIGINT : cancel threads | |
| | | |
| | Updates: .... | |
| | | |
| +-----------------------------------------------------------------------*/ |
| static void *handle_signals(void *arg) |
| { |
| sigset_t signals_set; |
| int thd; |
| int sig; |
| int retvalsig = 0; |
| |
| if (debug) |
| tst_resm(TINFO, "signal handler %lu started", pthread_self()); |
| /* |
| * Set up the signals that we want to handle... |
| */ |
| sigemptyset(&signals_set); |
| sigaddset(&signals_set, SIGINT); |
| sigaddset(&signals_set, SIGQUIT); |
| sigaddset(&signals_set, SIGTERM); |
| sigaddset(&signals_set, SIGUSR1); |
| sigaddset(&signals_set, SIGALRM); |
| while (1) { |
| if (debug) |
| tst_resm(TINFO, "Signal handler starts waiting..."); |
| |
| sigwait(&signals_set, &sig); |
| if (debug) |
| tst_resm(TINFO, "Signal handler caught signal %d", sig); |
| |
| switch (sig) { |
| case SIGALRM: |
| case SIGUSR1: |
| case SIGINT: |
| if (sig_cancel) |
| tst_resm(TINFO, |
| "Signal handler: already finished; " |
| "ignoring signal"); |
| else { |
| /* |
| * Have to signal all non started threads... |
| */ |
| |
| retvalsig = pthread_mutex_lock(&sig_mutex); |
| if (retvalsig != 0) |
| sys_error("handle_signal : mutex_lock(&sig_mutex) FAILED", __LINE__); |
| |
| sig_cancel = 1; |
| retvalsig = pthread_mutex_unlock (&sig_mutex); |
| if (retvalsig != 0) |
| sys_error("handle_signal : mutex_unlock(&sig_mutex) FAILED", __LINE__); |
| |
| /* |
| * ......... and all started |
| */ |
| for (thd = 0; thd < indice; thd++) { |
| if (debug) |
| tst_resm(TINFO, |
| "signal handler: " |
| "cancelling thread (%d of " |
| "%d)", thd, indice); |
| retvalsig = pthread_cancel (threads[thd]); |
| if (retvalsig != 0) |
| sys_error("handle_signal : cancel FAILED", __LINE__); |
| } |
| } |
| break; |
| case SIGQUIT: |
| tst_resm(TINFO, |
| "Signal handler: Caught SIGQUIT; doing nothing"); |
| break; |
| case SIGTERM: |
| tst_resm(TINFO, |
| "Signal handler: Caught SIGTERM; doing nothing"); |
| break; |
| default: |
| exit (1); |
| } |
| } |
| return NULL; |
| } |
| /*----------------------------------------------------------------------+ |
| | error () | |
| | =====================================================================| |
| | | |
| | Function: Prints out message and exits... | |
| | | |
| +----------------------------------------------------------------------*/ |
| static void error(const char *msg, int line) |
| { |
| tst_brkm(TFAIL, cleanup, "ERROR [line: %d] %s", line, msg); |
| } |
| |
| /*----------------------------------------------------------------------+ |
| | sys_error () | |
| | =====================================================================| |
| | | |
| | Function: Creates system error message and calls error () | |
| | | |
| +----------------------------------------------------------------------*/ |
| /* |
| * XXX (garrcoop): the way that this is being called is just plain wrong. |
| * pthread(5) returns 0 or errnos, not necessarily sets errno to a sensible |
| * value. |
| */ |
| static void sys_error(const char *msg, int line) |
| { |
| char syserr_msg[256]; |
| |
| sprintf(syserr_msg, "%s: %s", msg, strerror(errno)); |
| error(syserr_msg, line); |
| } |