205 lines
5.1 KiB
C
205 lines
5.1 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright © International Business Machines Corp., 2006, 2008
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
|
|
* the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* NAME
|
|
* async_handler.c
|
|
*
|
|
* DESCRIPTION
|
|
* Measure the latency involved in asynchronous event handlers.
|
|
* Specifically it measures the latency of the pthread_cond_signal
|
|
* call until the signalled thread is scheduled.
|
|
*
|
|
* USAGE:
|
|
* Use run_auto.sh script in current directory to build and run test.
|
|
*
|
|
* AUTHOR
|
|
* Darren Hart <dvhltc@us.ibm.com>
|
|
*
|
|
* HISTORY
|
|
* 2006-Oct-20: Initial version by Darren Hart <dvhltc@us.ibm.com>
|
|
*
|
|
* This line has to be added to avoid a stupid CVS problem
|
|
*****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <math.h>
|
|
#include <librttest.h>
|
|
#include <libstats.h>
|
|
#include <getopt.h>
|
|
|
|
#define SIGNAL_PRIO 89
|
|
#define HANDLER_PRIO 89
|
|
#define DEFAULT_ITERATIONS 1000000 /* about 1 minute @ 2GHz */
|
|
#define HIST_BUCKETS 100
|
|
#define PASS_US 100
|
|
|
|
static nsec_t start;
|
|
static nsec_t end;
|
|
static int iterations = 0;
|
|
|
|
#define CHILD_START 0
|
|
#define CHILD_WAIT 1
|
|
#define CHILD_HANDLED 2
|
|
#define CHILD_QUIT 3
|
|
atomic_t step;
|
|
|
|
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
|
|
pthread_mutex_t mutex;
|
|
|
|
static int ret = 0;
|
|
|
|
void usage(void)
|
|
{
|
|
rt_help();
|
|
printf("async_handler specific options:\n");
|
|
printf
|
|
(" -iITERATIONS number of iterations to calculate the average over\n");
|
|
}
|
|
|
|
int parse_args(int c, char *v)
|
|
{
|
|
|
|
int handled = 1;
|
|
switch (c) {
|
|
case 'h':
|
|
usage();
|
|
exit(0);
|
|
case 'i':
|
|
iterations = atoi(v);
|
|
break;
|
|
default:
|
|
handled = 0;
|
|
break;
|
|
}
|
|
return handled;
|
|
}
|
|
|
|
void *handler_thread(void *arg)
|
|
{
|
|
|
|
while (atomic_get(&step) != CHILD_QUIT) {
|
|
pthread_mutex_lock(&mutex);
|
|
atomic_set(CHILD_WAIT, &step);
|
|
if (pthread_cond_wait(&cond, &mutex) != 0) {
|
|
perror("pthead_cond_wait");
|
|
break;
|
|
}
|
|
end = rt_gettime();
|
|
atomic_set(CHILD_HANDLED, &step);
|
|
pthread_mutex_unlock(&mutex);
|
|
while (atomic_get(&step) == CHILD_HANDLED)
|
|
usleep(10);
|
|
}
|
|
printf("handler thread exiting\n");
|
|
return 0;
|
|
}
|
|
|
|
void *signal_thread(void *arg)
|
|
{
|
|
|
|
int i;
|
|
long delta, max, min;
|
|
stats_container_t dat;
|
|
stats_container_t hist;
|
|
stats_record_t rec;
|
|
|
|
stats_container_init(&dat, iterations);
|
|
stats_container_init(&hist, HIST_BUCKETS);
|
|
|
|
min = max = 0;
|
|
for (i = 0; i < iterations; i++) {
|
|
/* wait for child to wait on cond, then signal the event */
|
|
while (atomic_get(&step) != CHILD_WAIT)
|
|
usleep(10);
|
|
pthread_mutex_lock(&mutex);
|
|
start = rt_gettime();
|
|
if (pthread_cond_signal(&cond) != 0) {
|
|
perror("pthread_cond_signal");
|
|
atomic_set(CHILD_QUIT, &step);
|
|
break;
|
|
}
|
|
pthread_mutex_unlock(&mutex);
|
|
|
|
/* wait for the event handler to schedule */
|
|
while (atomic_get(&step) != CHILD_HANDLED)
|
|
usleep(10);
|
|
delta = (long)((end - start) / NS_PER_US);
|
|
if (delta > pass_criteria)
|
|
ret = 1;
|
|
rec.x = i;
|
|
rec.y = delta;
|
|
stats_container_append(&dat, rec);
|
|
if (i == 0)
|
|
min = max = delta;
|
|
else {
|
|
min = MIN(min, delta);
|
|
max = MAX(max, delta);
|
|
}
|
|
atomic_set((i == iterations - 1) ? CHILD_QUIT : CHILD_START,
|
|
&step);
|
|
}
|
|
printf("recording statistics...\n");
|
|
printf("Min: %ld us\n", min);
|
|
printf("Max: %ld us\n", max);
|
|
printf("Avg: %.4f us\n", stats_avg(&dat));
|
|
printf("StdDev: %.4f us\n", stats_stddev(&dat));
|
|
stats_hist(&hist, &dat);
|
|
stats_container_save("samples",
|
|
"Asynchronous Event Handling Latency Scatter Plot",
|
|
"Iteration", "Latency (us)", &dat, "points");
|
|
stats_container_save("hist",
|
|
"Asynchronous Event Handling Latency Histogram",
|
|
"Latency (us)", "Samples", &hist, "steps");
|
|
printf("signal thread exiting\n");
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int signal_id, handler_id;
|
|
setup();
|
|
|
|
printf("\n-----------------------------------\n");
|
|
printf("Asynchronous Event Handling Latency\n");
|
|
printf("-----------------------------------\n\n");
|
|
|
|
pass_criteria = PASS_US;
|
|
rt_init("i:h", parse_args, argc, argv);
|
|
|
|
init_pi_mutex(&mutex);
|
|
|
|
atomic_set(CHILD_START, &step);
|
|
|
|
if (iterations == 0)
|
|
iterations = DEFAULT_ITERATIONS;
|
|
printf("Running %d iterations\n", iterations);
|
|
|
|
handler_id =
|
|
create_fifo_thread(handler_thread, NULL, HANDLER_PRIO);
|
|
signal_id = create_fifo_thread(signal_thread, NULL, SIGNAL_PRIO);
|
|
|
|
join_threads();
|
|
|
|
printf("\nCriteria: latencies < %d\n", (int)pass_criteria);
|
|
printf("Result: %s\n", ret ? "FAIL" : "PASS");
|
|
|
|
return ret;
|
|
}
|