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