| /****************************************************************************** |
| * |
| * Copyright © International Business Machines Corp., 2005-2008 |
| * |
| * 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 |
| * |
| * NAME |
| * pthread_cond_many.c |
| * |
| * DESCRIPTION |
| * Measure pthread_cond_t latencies , but in presence of many processes. |
| * |
| * USAGE: |
| * Use run_auto.sh script in current directory to build and run test. |
| * |
| * AUTHOR |
| * Paul E. McKenney <[email protected]> |
| * |
| * HISTORY |
| * librttest parsing, threading, and mutex initialization - Darren Hart |
| * |
| * |
| * This line has to be added to avoid a stupid CVS problem |
| *****************************************************************************/ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <pthread.h> |
| #include <sys/time.h> |
| #include <sched.h> |
| #include <string.h> |
| #include <sys/poll.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| #include <librttest.h> |
| #include <libstats.h> |
| #define PASS_US 100 |
| pthread_mutex_t child_mutex; |
| volatile int *child_waiting = NULL; |
| double endtime; |
| pthread_cond_t *condlist = NULL; |
| int iterations = 0; |
| int nthreads = 0; |
| int realtime = 0; |
| int broadcast_flag = 0; |
| unsigned long latency = 0; |
| int fail = 0; |
| /* |
| * Return time as a floating-point number rather than struct timeval. |
| */ |
| |
| double d_gettimeofday(void) |
| { |
| int retval; |
| struct timeval tv; |
| |
| retval = gettimeofday(&tv, NULL); |
| if (retval != 0) { |
| perror("gettimeofday"); |
| exit(-1); |
| } |
| return (tv.tv_sec + ((double)tv.tv_usec) / 1000000.); |
| } |
| |
| void * |
| childfunc(void *arg) |
| { |
| int myid = (intptr_t)arg; |
| pthread_cond_t *cp; |
| volatile int *cw; |
| |
| cp = &condlist[myid]; |
| cw = &child_waiting[myid]; |
| while (*cw == 0) { |
| pthread_mutex_lock(&child_mutex); |
| *cw = 1; |
| if (pthread_cond_wait(cp, &child_mutex) != 0) { |
| perror("pthread_cond_wait"); |
| exit(-1); |
| } |
| endtime = d_gettimeofday(); |
| *cw = 2; |
| pthread_mutex_unlock(&child_mutex); |
| while (*cw == 2) { |
| poll(NULL, 0, 10); |
| } |
| } |
| pthread_exit(NULL); |
| } |
| |
| pthread_t |
| create_thread_(int itsid) |
| { |
| pthread_attr_t attr; |
| pthread_t childid; |
| int prio; |
| struct sched_param schparm; |
| |
| if (pthread_attr_init(&attr) != 0) { |
| perror("pthread_attr_init"); |
| exit(-1); |
| } |
| if (realtime) { |
| prio = sched_get_priority_max(SCHED_FIFO); |
| if (prio == -1) { |
| perror("sched_get_priority_max"); |
| exit(-1); |
| } |
| schparm.sched_priority = prio; |
| if (sched_setscheduler(getpid(), SCHED_FIFO, &schparm) != 0) { |
| perror("sched_setscheduler"); |
| exit(-1); |
| } |
| |
| if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) != 0) { |
| perror("pthread_attr_setschedpolicy"); |
| exit(-1); |
| } |
| if (pthread_attr_setschedparam(&attr, &schparm) != 0) { |
| perror("pthread_attr_setschedparam"); |
| exit(-1); |
| } |
| } |
| if (pthread_attr_setstacksize(&attr, (size_t)(32*1024)) != 0) { |
| perror("pthread_attr_setstacksize"); |
| exit(-1); |
| } |
| if (pthread_cond_init(&condlist[itsid], NULL) != 0) { |
| perror("pthread_cond_init"); |
| exit(-1); |
| } |
| if (pthread_create(&childid, &attr, childfunc, (void *)(intptr_t)itsid) != 0) { |
| perror("pthread_create"); |
| exit(-1); |
| } |
| return (childid); |
| } |
| |
| void |
| wake_child(int itsid, int broadcast_flag) |
| { |
| double starttime; |
| |
| pthread_mutex_lock(&child_mutex); |
| while (child_waiting[itsid] == 0) { |
| pthread_mutex_unlock(&child_mutex); |
| sched_yield(); |
| pthread_mutex_lock(&child_mutex); |
| } |
| pthread_mutex_unlock(&child_mutex); |
| if (broadcast_flag) { |
| starttime = d_gettimeofday(); |
| if (pthread_cond_broadcast(&condlist[itsid]) != 0) { |
| perror("pthread_cond_broadcast"); |
| exit(-1); |
| } |
| } else { |
| starttime = d_gettimeofday(); |
| if (pthread_cond_signal(&condlist[itsid]) != 0) { |
| perror("pthread_cond_signal"); |
| exit(-1); |
| } |
| } |
| for (;;) { |
| pthread_mutex_lock(&child_mutex); |
| if (child_waiting[itsid] == 2) { |
| break; |
| } |
| pthread_mutex_unlock(&child_mutex); |
| poll(NULL, 0, 10); |
| } |
| latency = (unsigned long)((endtime - starttime) * 1000000.); |
| pthread_mutex_unlock(&child_mutex); |
| } |
| |
| void |
| test_signal(long iter, long nthreads) |
| { |
| int i; |
| int j; |
| int k; |
| pthread_t *pt; |
| unsigned long max = 0; |
| unsigned long min = 0; |
| stats_container_t dat; |
| stats_record_t rec; |
| |
| stats_container_init(&dat,iter * nthreads); |
| |
| pt = (pthread_t *)malloc(sizeof(*pt) * nthreads); |
| if (pt == NULL) { |
| fprintf(stderr, "Out of memory\n"); |
| exit(-1); |
| } |
| for (j = 0; j < nthreads; j++) { |
| child_waiting[j] = 0; |
| pt[j] = create_thread_(j); |
| } |
| for (i = 0; i < (iter - 1) * nthreads; i+=nthreads) { |
| for (j = 0 , k = i; j < nthreads; j++ , k++) { |
| wake_child(j, broadcast_flag); |
| rec.x = k; |
| rec.y = latency; |
| stats_container_append(&dat, rec); |
| pthread_mutex_lock(&child_mutex); |
| child_waiting[j] = 0; |
| pthread_mutex_unlock(&child_mutex); |
| } |
| } |
| for (j = 0; j < nthreads; j++) { |
| wake_child(j, broadcast_flag); |
| pthread_mutex_lock(&child_mutex); |
| child_waiting[j] = 3; |
| pthread_mutex_unlock(&child_mutex); |
| if (pthread_join(pt[j], NULL) != 0) { |
| fprintf(stderr, "%d: ", j); |
| perror("pthread_join"); |
| exit(-1); |
| } |
| } |
| min = (unsigned long)-1; |
| for (i = 0; i < iter * nthreads; i++) { |
| latency = dat.records[i].y; |
| if (latency > PASS_US) |
| fail = 1; |
| min = MIN(min, latency); |
| max = MAX(max, latency); |
| } |
| printf("Recording statistics...\n"); |
| printf("Minimum: %lu us\n", min); |
| printf("Maximum: %lu us\n", max); |
| printf("Average: %f us\n", stats_avg(&dat)); |
| printf("Standard Deviation: %f\n", stats_stddev(&dat)); |
| } |
| |
| void usage(void) |
| { |
| rt_help(); |
| printf("pthread_cond_many specific options:\n"); |
| printf(" -r,--realtime run with realtime priority\n"); |
| printf(" -b,--broadcast use cond_broadcast instead of cond_signal\n"); |
| printf(" -iITERATIONS iterations (required)\n"); |
| printf(" -nNTHREADS number of threads (required)\n"); |
| printf("deprecated unnamed arguments:\n"); |
| printf(" pthread_cond_many [options] iterations nthreads\n"); |
| } |
| |
| int parse_args(int c, char *v) |
| { |
| int handled; |
| switch (c) { |
| case 'h': |
| usage(); |
| exit(0); |
| case 'a': |
| broadcast_flag = 1; |
| break; |
| case 'i': |
| iterations = atoi(v); |
| break; |
| case 'n': |
| nthreads = atoi(v); |
| break; |
| case 'r': |
| realtime = 1; |
| break; |
| default: |
| handled = 0; |
| break; |
| } |
| return handled; |
| } |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| struct option longopts[] = { |
| {"broadcast", 0, NULL, 'a'}, |
| {"realtime", 0, NULL, 'r'}, |
| {NULL, 0, NULL, 0}, |
| }; |
| setup(); |
| |
| init_pi_mutex(&child_mutex); |
| rt_init_long("ahi:n:r", longopts, parse_args, argc, argv); |
| |
| /* Legacy command line arguments support, overrides getopt args. */ |
| if (optind < argc) |
| iterations = strtol(argv[optind++], NULL, 0); |
| if (optind < argc) |
| nthreads = strtol(argv[optind++], NULL, 0); |
| |
| /* Ensure we have the required arguments. */ |
| if (iterations == 0 || nthreads == 0) { |
| usage(); |
| exit(1); |
| } |
| |
| child_waiting = (int *)malloc(sizeof(*child_waiting) * nthreads); |
| condlist = (pthread_cond_t *)malloc(sizeof(*condlist) * nthreads); |
| if ((child_waiting == NULL) || (condlist == NULL)) { |
| fprintf(stderr, "Out of memory\n"); |
| exit(-1); |
| } |
| test_signal(iterations, nthreads); |
| printf("\nCriteria: latencies < %d us\n", PASS_US); |
| printf("Result: %s\n", fail ? "FAIL" : "PASS"); |
| |
| return 0; |
| } |