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
52static nsec_t start;
53static nsec_t end;
54static int iterations = 0;
55
56#define CHILD_START   0
57#define CHILD_WAIT    1
58#define CHILD_HANDLED 2
59#define CHILD_QUIT    3
60atomic_t step;
61
62pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
63pthread_mutex_t mutex;
64
65static int ret = 0;
66
67void 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
75int 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
93void *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
113void *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
174int 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