1 /******************************************************************************
2 *
3 * Copyright © International Business Machines Corp., 2006, 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 * async_handler.c
21 *
22 * DESCRIPTION
23 * Measure the latency involved in asynchronous event handlers.
24 * Specifically it measures the latency of the pthread_cond_signal
25 * call until the signalled thread is scheduled.
26 *
27 * USAGE:
28 * Use run_auto.sh script in current directory to build and run test.
29 *
30 * AUTHOR
31 * Darren Hart <dvhltc@us.ibm.com>
32 *
33 * HISTORY
34 * 2006-Oct-20: Initial version by Darren Hart <dvhltc@us.ibm.com>
35 *
36 * This line has to be added to avoid a stupid CVS problem
37 *****************************************************************************/
38
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <math.h>
42 #include <librttest.h>
43 #include <libstats.h>
44 #include <getopt.h>
45
46 #define SIGNAL_PRIO 89
47 #define HANDLER_PRIO 89
48 #define DEFAULT_ITERATIONS 1000000 /* about 1 minute @ 2GHz */
49 #define HIST_BUCKETS 100
50 #define PASS_US 100
51
52 static nsec_t start;
53 static nsec_t end;
54 static int iterations = 0;
55
56 #define CHILD_START 0
57 #define CHILD_WAIT 1
58 #define CHILD_HANDLED 2
59 #define CHILD_QUIT 3
60 atomic_t step;
61
62 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
63 pthread_mutex_t mutex;
64
65 static int ret = 0;
66
usage(void)67 void usage(void)
68 {
69 rt_help();
70 printf("async_handler specific options:\n");
71 printf
72 (" -iITERATIONS number of iterations to calculate the average over\n");
73 }
74
parse_args(int c,char * v)75 int parse_args(int c, char *v)
76 {
77
78 int handled = 1;
79 switch (c) {
80 case 'h':
81 usage();
82 exit(0);
83 case 'i':
84 iterations = atoi(v);
85 break;
86 default:
87 handled = 0;
88 break;
89 }
90 return handled;
91 }
92
handler_thread(void * arg)93 void *handler_thread(void *arg)
94 {
95
96 while (atomic_get(&step) != CHILD_QUIT) {
97 pthread_mutex_lock(&mutex);
98 atomic_set(CHILD_WAIT, &step);
99 if (pthread_cond_wait(&cond, &mutex) != 0) {
100 perror("pthead_cond_wait");
101 break;
102 }
103 end = rt_gettime();
104 atomic_set(CHILD_HANDLED, &step);
105 pthread_mutex_unlock(&mutex);
106 while (atomic_get(&step) == CHILD_HANDLED)
107 usleep(10);
108 }
109 printf("handler thread exiting\n");
110 return 0;
111 }
112
signal_thread(void * arg)113 void *signal_thread(void *arg)
114 {
115
116 int i;
117 long delta, max, min;
118 stats_container_t dat;
119 stats_container_t hist;
120 stats_record_t rec;
121
122 stats_container_init(&dat, iterations);
123 stats_container_init(&hist, HIST_BUCKETS);
124
125 min = max = 0;
126 for (i = 0; i < iterations; i++) {
127 /* wait for child to wait on cond, then signal the event */
128 while (atomic_get(&step) != CHILD_WAIT)
129 usleep(10);
130 pthread_mutex_lock(&mutex);
131 start = rt_gettime();
132 if (pthread_cond_signal(&cond) != 0) {
133 perror("pthread_cond_signal");
134 atomic_set(CHILD_QUIT, &step);
135 break;
136 }
137 pthread_mutex_unlock(&mutex);
138
139 /* wait for the event handler to schedule */
140 while (atomic_get(&step) != CHILD_HANDLED)
141 usleep(10);
142 delta = (long)((end - start) / NS_PER_US);
143 if (delta > pass_criteria)
144 ret = 1;
145 rec.x = i;
146 rec.y = delta;
147 stats_container_append(&dat, rec);
148 if (i == 0)
149 min = max = delta;
150 else {
151 min = MIN(min, delta);
152 max = MAX(max, delta);
153 }
154 atomic_set((i == iterations - 1) ? CHILD_QUIT : CHILD_START,
155 &step);
156 }
157 printf("recording statistics...\n");
158 printf("Min: %ld us\n", min);
159 printf("Max: %ld us\n", max);
160 printf("Avg: %.4f us\n", stats_avg(&dat));
161 printf("StdDev: %.4f us\n", stats_stddev(&dat));
162 stats_hist(&hist, &dat);
163 stats_container_save("samples",
164 "Asynchronous Event Handling Latency Scatter Plot",
165 "Iteration", "Latency (us)", &dat, "points");
166 stats_container_save("hist",
167 "Asynchronous Event Handling Latency Histogram",
168 "Latency (us)", "Samples", &hist, "steps");
169 printf("signal thread exiting\n");
170
171 return NULL;
172 }
173
main(int argc,char * argv[])174 int main(int argc, char *argv[])
175 {
176 int signal_id, handler_id;
177 setup();
178
179 printf("\n-----------------------------------\n");
180 printf("Asynchronous Event Handling Latency\n");
181 printf("-----------------------------------\n\n");
182
183 pass_criteria = PASS_US;
184 rt_init("i:h", parse_args, argc, argv);
185
186 init_pi_mutex(&mutex);
187
188 atomic_set(CHILD_START, &step);
189
190 if (iterations == 0)
191 iterations = DEFAULT_ITERATIONS;
192 printf("Running %d iterations\n", iterations);
193
194 handler_id =
195 create_fifo_thread(handler_thread, NULL, HANDLER_PRIO);
196 signal_id = create_fifo_thread(signal_thread, NULL, SIGNAL_PRIO);
197
198 join_threads();
199
200 printf("\nCriteria: latencies < %d\n", (int)pass_criteria);
201 printf("Result: %s\n", ret ? "FAIL" : "PASS");
202
203 return ret;
204 }
205