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_tsc.c
21 *
22 * DESCRIPTION
23 * This test mimics an async event handler in a real-time JVM
24 * An async event server thread is created that goes to sleep waiting
25 * to be woken up to do some work.
26 *
27 * A user thread is created that simulates the firing of an event by
28 * signalling the async handler thread to do some work.
29 *
30 * USAGE:
31 * Use run_auto.sh script in current directory to build and run test.
32 *
33 * AUTHOR
34 * Darren Hart <dvhltc@us.ibm.com>
35 *
36 * HISTORY
37 * 2006-Oct-20: Initial version by Darren Hart <dvhltc@us.ibm.com>
38 *
39 * This line has to be added to avoid a stupid CVS problem
40 *****************************************************************************/
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <math.h>
45 #include <stdint.h>
46 #include <pthread.h>
47 #include <librttest.h>
48 #include <libstats.h>
49 #include <libtsc.h>
50
51 #define HANDLER_PRIO 98
52 #define SIGNAL_PRIO 99
53 #define ITERATIONS 10000000
54 #define HIST_BUCKETS 100
55 #define PASS_US 100
56
57 nsec_t start;
58 nsec_t end;
59 unsigned long long tsc_period; /* in picoseconds */
60 int over_20 = 0;
61 int over_25 = 0;
62 int over_30 = 0;
63
64 #define CHILD_START 0
65 #define CHILD_WAIT 1
66 #define CHILD_HANDLED 2
67 #define CHILD_QUIT 3
68 atomic_t step;
69
70 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
71 pthread_mutex_t mutex;
72
usage(void)73 void usage(void)
74 {
75 rt_help();
76 printf("async_handler_tsc specific options:\n");
77 }
78
parse_args(int c,char * v)79 int parse_args(int c, char *v)
80 {
81
82 int handled = 1;
83 switch (c) {
84 case 'h':
85 usage();
86 exit(0);
87 default:
88 handled = 0;
89 break;
90 }
91 return handled;
92 }
93
94 /* calculate the tsc period */
tsc_period_ps(void)95 unsigned long long tsc_period_ps(void)
96 {
97 nsec_t ns_start;
98 nsec_t ns_end;
99 unsigned long long tsc_start, tsc_end;
100
101 rdtscll(tsc_start);
102 ns_start = rt_gettime();
103 sleep(1);
104 rdtscll(tsc_end);
105 ns_end = rt_gettime();
106
107 return (1000 * (ns_end - ns_start)) / tsc_minus(tsc_start, tsc_end);
108 }
109
handler_thread(void * arg)110 void *handler_thread(void *arg)
111 {
112 while (atomic_get(&step) != CHILD_QUIT) {
113 pthread_mutex_lock(&mutex);
114 atomic_set(CHILD_WAIT, &step);
115 if (pthread_cond_wait(&cond, &mutex) != 0) {
116 perror("pthead_cond_wait");
117 break;
118 }
119 rdtscll(end);
120 atomic_set(CHILD_HANDLED, &step);
121 pthread_mutex_unlock(&mutex);
122 while (atomic_get(&step) == CHILD_HANDLED)
123 usleep(10);
124 }
125 printf("handler thread exiting\n");
126 return NULL;
127 }
128
signal_thread(void * arg)129 void *signal_thread(void *arg)
130 {
131 int i;
132 long delta, max, min;
133 stats_container_t dat;
134 stats_container_t hist;
135 stats_record_t rec;
136
137 stats_container_init(&dat, ITERATIONS);
138 stats_container_init(&hist, HIST_BUCKETS);
139
140 min = max = 0;
141 for (i = 0; i < ITERATIONS; i++) {
142 /* wait for child to wait on cond, then signal the event */
143 while (atomic_get(&step) != CHILD_WAIT)
144 usleep(10);
145 pthread_mutex_lock(&mutex);
146 rdtscll(start);
147 if (pthread_cond_signal(&cond) != 0) {
148 perror("pthread_cond_signal");
149 atomic_set(CHILD_QUIT, &step);
150 break;
151 }
152 pthread_mutex_unlock(&mutex);
153
154 /* wait for the event handler to schedule */
155 while (atomic_get(&step) != CHILD_HANDLED)
156 usleep(10);
157 delta = (long)(tsc_period * (end - start) / 1000000);
158 if (delta > 30) {
159 over_30++;
160 } else if (delta > 25) {
161 over_25++;
162 } else if (delta > 20) {
163 over_20++;
164 }
165 rec.x = i;
166 rec.y = delta;
167 stats_container_append(&dat, rec);
168 if (i == 0)
169 min = max = delta;
170 else {
171 min = MIN(min, delta);
172 max = MAX(max, delta);
173 }
174 atomic_set((i == ITERATIONS - 1) ? CHILD_QUIT : CHILD_START,
175 &step);
176 }
177 printf("recording statistics...\n");
178 printf("Minimum: %ld\n", min);
179 printf("Maximum: %ld\n", max);
180 printf("Average: %f\n", stats_avg(&dat));
181 printf("Standard Deviation: %f\n", stats_stddev(&dat));
182 stats_hist(&hist, &dat);
183 stats_container_save("samples",
184 "Asynchronous Event Handling Latency (TSC) Scatter Plot",
185 "Iteration", "Latency (us)", &dat, "points");
186 stats_container_save("hist",
187 "Asynchronous Event Handling Latency (TSC) Histogram",
188 "Latency (us)", "Samples", &hist, "steps");
189 printf("signal thread exiting\n");
190
191 return NULL;
192 }
193
main(int argc,char * argv[])194 int main(int argc, char *argv[])
195 {
196 int signal_id, handler_id;
197
198 #ifdef TSC_UNSUPPORTED
199 printf("Error: test cannot be executed on an arch wihout TSC.\n");
200 return ENOTSUP;
201 #endif
202 setup();
203
204 rt_init("h", parse_args, argc, argv);
205
206 printf("-------------------------------\n");
207 printf("Asynchronous Event Handling Latency\n");
208 printf("-------------------------------\n\n");
209 printf("Running %d iterations\n", ITERATIONS);
210 printf("Calculating tsc period...");
211 fflush(stdout);
212 tsc_period = tsc_period_ps();
213 printf("%llu ps\n", tsc_period);
214
215 init_pi_mutex(&mutex);
216
217 atomic_set(CHILD_START, &step);
218 handler_id =
219 create_fifo_thread(handler_thread, NULL, HANDLER_PRIO);
220 signal_id = create_fifo_thread(signal_thread, NULL, SIGNAL_PRIO);
221
222 join_threads();
223
224 printf("%d samples over 20 us latency\n", over_20);
225 printf("%d samples over 25 us latency\n", over_25);
226 printf("%d samples over 30 us latency\n", over_30);
227
228 return 0;
229 }
230