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