1 /******************************************************************************
2  *
3  *   Copyright © International Business Machines  Corp., 2005-2008
4  *
5  *   This program is free software;  you can redistribute it and/or modify
6  *   it under the terms of the GNU General Public License as published by
7  *   the Free Software Foundation; either version 2 of the License, or
8  *   (at your option) any later version.
9  *
10  *   This program is distributed in the hope that it will be useful,
11  *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
13  *   the GNU General Public License for more details.
14  *
15  *   You should have received a copy of the GNU General Public License
16  *   along with this program;  if not, write to the Free Software
17  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18  *
19  * NAME
20  *      pthread_cond_many.c
21  *
22  * DESCRIPTION
23  *      Measure pthread_cond_t latencies , but in presence of many processes.
24  *
25  * USAGE:
26  *      Use run_auto.sh script in current directory to build and run test.
27  *
28  * AUTHOR
29  *      Paul E. McKenney <paulmck@us.ibm.com>
30  *
31  * HISTORY
32  *      librttest parsing, threading, and mutex initialization - Darren Hart
33  *
34  *
35  *      This line has to be added to avoid a stupid CVS problem
36  *****************************************************************************/
37 
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <pthread.h>
41 #include <sys/time.h>
42 #include <sched.h>
43 #include <string.h>
44 #include <sys/poll.h>
45 #include <sys/types.h>
46 #include <unistd.h>
47 #include <librttest.h>
48 #include <libstats.h>
49 #define PASS_US 100
50 pthread_mutex_t child_mutex;
51 volatile int *child_waiting = NULL;
52 double endtime;
53 pthread_cond_t *condlist = NULL;
54 int iterations = 0;
55 int nthreads = 0;
56 int realtime = 0;
57 int broadcast_flag = 0;
58 unsigned long latency = 0;
59 int fail = 0;
60 /*
61  * Return time as a floating-point number rather than struct timeval.
62  */
63 
d_gettimeofday(void)64 double d_gettimeofday(void)
65 {
66 	int retval;
67 	struct timeval tv;
68 
69 	retval = gettimeofday(&tv, NULL);
70 	if (retval != 0) {
71 		perror("gettimeofday");
72 		exit(-1);
73 	}
74 	return (tv.tv_sec + ((double)tv.tv_usec) / 1000000.);
75 }
76 
childfunc(void * arg)77 void *childfunc(void *arg)
78 {
79 	int myid = (intptr_t) arg;
80 	pthread_cond_t *cp;
81 	volatile int *cw;
82 
83 	cp = &condlist[myid];
84 	cw = &child_waiting[myid];
85 	while (*cw == 0) {
86 		pthread_mutex_lock(&child_mutex);
87 		*cw = 1;
88 		if (pthread_cond_wait(cp, &child_mutex) != 0) {
89 			perror("pthread_cond_wait");
90 			exit(-1);
91 		}
92 		endtime = d_gettimeofday();
93 		*cw = 2;
94 		pthread_mutex_unlock(&child_mutex);
95 		while (*cw == 2) {
96 			poll(NULL, 0, 10);
97 		}
98 	}
99 	pthread_exit(NULL);
100 }
101 
create_thread_(int itsid)102 pthread_t create_thread_(int itsid)
103 {
104 	pthread_attr_t attr;
105 	pthread_t childid;
106 	int prio;
107 	struct sched_param schparm;
108 
109 	if (pthread_attr_init(&attr) != 0) {
110 		perror("pthread_attr_init");
111 		exit(-1);
112 	}
113 	if (realtime) {
114 		prio = sched_get_priority_max(SCHED_FIFO);
115 		if (prio == -1) {
116 			perror("sched_get_priority_max");
117 			exit(-1);
118 		}
119 		schparm.sched_priority = prio;
120 		if (sched_setscheduler(getpid(), SCHED_FIFO, &schparm) != 0) {
121 			perror("sched_setscheduler");
122 			exit(-1);
123 		}
124 
125 		if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) != 0) {
126 			perror("pthread_attr_setschedpolicy");
127 			exit(-1);
128 		}
129 		if (pthread_attr_setschedparam(&attr, &schparm) != 0) {
130 			perror("pthread_attr_setschedparam");
131 			exit(-1);
132 		}
133 	}
134 	if (pthread_attr_setstacksize(&attr, (size_t) (32 * 1024)) != 0) {
135 		perror("pthread_attr_setstacksize");
136 		exit(-1);
137 	}
138 	if (pthread_cond_init(&condlist[itsid], NULL) != 0) {
139 		perror("pthread_cond_init");
140 		exit(-1);
141 	}
142 	if (pthread_create(&childid, &attr, childfunc, (void *)(intptr_t) itsid)
143 	    != 0) {
144 		perror("pthread_create");
145 		exit(-1);
146 	}
147 	return (childid);
148 }
149 
wake_child(int itsid,int broadcast_flag)150 void wake_child(int itsid, int broadcast_flag)
151 {
152 	double starttime;
153 
154 	pthread_mutex_lock(&child_mutex);
155 	while (child_waiting[itsid] == 0) {
156 		pthread_mutex_unlock(&child_mutex);
157 		sched_yield();
158 		pthread_mutex_lock(&child_mutex);
159 	}
160 	pthread_mutex_unlock(&child_mutex);
161 	if (broadcast_flag) {
162 		starttime = d_gettimeofday();
163 		if (pthread_cond_broadcast(&condlist[itsid]) != 0) {
164 			perror("pthread_cond_broadcast");
165 			exit(-1);
166 		}
167 	} else {
168 		starttime = d_gettimeofday();
169 		if (pthread_cond_signal(&condlist[itsid]) != 0) {
170 			perror("pthread_cond_signal");
171 			exit(-1);
172 		}
173 	}
174 	for (;;) {
175 		pthread_mutex_lock(&child_mutex);
176 		if (child_waiting[itsid] == 2) {
177 			break;
178 		}
179 		pthread_mutex_unlock(&child_mutex);
180 		poll(NULL, 0, 10);
181 	}
182 	latency = (unsigned long)((endtime - starttime) * 1000000.);
183 	pthread_mutex_unlock(&child_mutex);
184 }
185 
test_signal(long iter,long nthreads)186 void test_signal(long iter, long nthreads)
187 {
188 	int i;
189 	int j;
190 	int k;
191 	pthread_t *pt;
192 	unsigned long max = 0;
193 	unsigned long min = 0;
194 	stats_container_t dat;
195 	stats_record_t rec;
196 
197 	stats_container_init(&dat, iter * nthreads);
198 
199 	pt = malloc(sizeof(*pt) * nthreads);
200 	if (pt == NULL) {
201 		fprintf(stderr, "Out of memory\n");
202 		exit(-1);
203 	}
204 	for (j = 0; j < nthreads; j++) {
205 		child_waiting[j] = 0;
206 		pt[j] = create_thread_(j);
207 	}
208 	for (i = 0; i < (iter - 1) * nthreads; i += nthreads) {
209 		for (j = 0, k = i; j < nthreads; j++, k++) {
210 			wake_child(j, broadcast_flag);
211 			rec.x = k;
212 			rec.y = latency;
213 			stats_container_append(&dat, rec);
214 			pthread_mutex_lock(&child_mutex);
215 			child_waiting[j] = 0;
216 			pthread_mutex_unlock(&child_mutex);
217 		}
218 	}
219 	for (j = 0; j < nthreads; j++) {
220 		wake_child(j, broadcast_flag);
221 		pthread_mutex_lock(&child_mutex);
222 		child_waiting[j] = 3;
223 		pthread_mutex_unlock(&child_mutex);
224 		if (pthread_join(pt[j], NULL) != 0) {
225 			fprintf(stderr, "%d: ", j);
226 			perror("pthread_join");
227 			exit(-1);
228 		}
229 	}
230 	min = (unsigned long)-1;
231 	for (i = 0; i < iter * nthreads; i++) {
232 		latency = dat.records[i].y;
233 		if (latency > PASS_US)
234 			fail = 1;
235 		min = MIN(min, latency);
236 		max = MAX(max, latency);
237 	}
238 	printf("Recording statistics...\n");
239 	printf("Minimum: %lu us\n", min);
240 	printf("Maximum: %lu us\n", max);
241 	printf("Average: %f us\n", stats_avg(&dat));
242 	printf("Standard Deviation: %f\n", stats_stddev(&dat));
243 }
244 
usage(void)245 void usage(void)
246 {
247 	rt_help();
248 	printf("pthread_cond_many specific options:\n");
249 	printf("  -r,--realtime   run with realtime priority\n");
250 	printf("  -b,--broadcast  use cond_broadcast instead of cond_signal\n");
251 	printf("  -iITERATIONS    iterations (required)\n");
252 	printf("  -nNTHREADS      number of threads (required)\n");
253 	printf("deprecated unnamed arguments:\n");
254 	printf("  pthread_cond_many [options] iterations nthreads\n");
255 }
256 
parse_args(int c,char * v)257 int parse_args(int c, char *v)
258 {
259 	int handled = 1;
260 	switch (c) {
261 	case 'h':
262 		usage();
263 		exit(0);
264 	case 'a':
265 		broadcast_flag = 1;
266 		break;
267 	case 'i':
268 		iterations = atoi(v);
269 		break;
270 	case 'n':
271 		nthreads = atoi(v);
272 		break;
273 	case 'r':
274 		realtime = 1;
275 		break;
276 	default:
277 		handled = 0;
278 		break;
279 	}
280 	return handled;
281 }
282 
main(int argc,char * argv[])283 int main(int argc, char *argv[])
284 {
285 	struct option longopts[] = {
286 		{"broadcast", 0, NULL, 'a'},
287 		{"realtime", 0, NULL, 'r'},
288 		{NULL, 0, NULL, 0},
289 	};
290 	setup();
291 
292 	init_pi_mutex(&child_mutex);
293 	rt_init_long("ahi:n:r", longopts, parse_args, argc, argv);
294 
295 	/* Legacy command line arguments support, overrides getopt args. */
296 	if (optind < argc)
297 		iterations = strtol(argv[optind++], NULL, 0);
298 	if (optind < argc)
299 		nthreads = strtol(argv[optind++], NULL, 0);
300 
301 	/* Ensure we have the required arguments. */
302 	if (iterations == 0 || nthreads == 0) {
303 		usage();
304 		exit(1);
305 	}
306 
307 	child_waiting = malloc(sizeof(*child_waiting) * nthreads);
308 	condlist = malloc(sizeof(*condlist) * nthreads);
309 	if ((child_waiting == NULL) || (condlist == NULL)) {
310 		fprintf(stderr, "Out of memory\n");
311 		exit(-1);
312 	}
313 	test_signal(iterations, nthreads);
314 	printf("\nCriteria: latencies < %d us\n", PASS_US);
315 	printf("Result: %s\n", fail ? "FAIL" : "PASS");
316 
317 	return 0;
318 }
319