in benchmarks/cyclictest/cyclictest.c [837:1114]
int main(int argc, char *argv[])
{
int i;
int ret;
struct thread_param_s **params = NULL;
struct thread_stats_s **stats = NULL;
struct sigevent event;
struct timer_notify_s tnotify;
uint32_t maxtimeout_timer;
uint32_t reqtimeout_timer;
running = true;
config.clock = CLOCK_MONOTONIC;
config.distance = 500;
config.duration = 0;
config.histogram = 0;
config.histofall = 0;
config.interval = 1000;
config.loops = 0;
config.threads = 1;
config.prio = 0;
config.policy = SCHED_FIFO;
config.meas_method = M_GETTIME;
config.wait_method = W_NANOSLEEP;
config.timer_dev = NULL;
config.quiet = false;
if (!parse_args(argc, argv))
{
print_help();
return ERROR;
}
if (!check_args_logic())
{
print_help();
return ERROR;
}
/* Timer must be configured */
if (config.wait_method == W_DEVTIMER || config.meas_method == M_TIMER_API)
{
timerfd = open(config.timer_dev, O_RDWR);
if (timerfd < 0)
{
perror("Failed to open the device timer");
return ERROR;
}
/* Configure the timer notification */
polltimer[0].fd = timerfd;
polltimer[0].events = POLLIN;
/* Fill in the notify struct
* We do not want any signalling. But we must configure it,
* because without it the timer will not start.
*/
memset(&event, 0, sizeof(event));
event.sigev_notify = SIGEV_NONE;
tnotify.periodic = true;
tnotify.pid = getpid();
tnotify.event = event;
/* Now set timeout of the timer.
* This depends on several factors.
*
* If wait_method == W_DEVTIMER, the timeout is set to config.interval
* (to achieve periodic operation). The extra time is measured by
* NANOSLEEP or the timer itself. If the timer is used, the timer
* zeroes itself when the timeout is reached, so we just get
* the timer value after poll has stopped blocking.
*
* If wait_method != W_DEVTIMER, we must set the timeout to at least
* the double of the maximum of all thread intervals
* (if you're not sure, please consult Claude Shannon).
*
* This raises the question: what if wait_method == W_DEVTIMER
* and meas_method == W_TIMER_API and the thread wakes up later
* then the timer's timeout? The solution is to have a different
* timer which runs slower and can measure overruns.
* But this would overcomplicate things.
*/
if (config.wait_method == W_DEVTIMER)
{
reqtimeout_timer = config.interval;
}
else if (config.wait_method == W_NANOSLEEP)
{
/* Multiply by 3 instead of 2, just to be sure */
reqtimeout_timer = 3 * (config.interval +
(config.threads - 1) * config.distance);
}
ret = ioctl(timerfd, TCIOC_MAXTIMEOUT,
(unsigned long)((uintptr_t)&maxtimeout_timer));
if (ret < 0)
{
perror("TCIOC_MAXTIMEOUT");
goto errtimer;
}
if (reqtimeout_timer > maxtimeout_timer)
{
fprintf(stderr, "The timer cannot measure such periods!\n");
goto errtimer;
}
ret = ioctl(timerfd, TCIOC_SETTIMEOUT,
(unsigned long)reqtimeout_timer);
if (ret < 0)
{
perror("TCIOC_SETTIMEOUT");
goto errtimer;
}
ret = ioctl(timerfd, TCIOC_NOTIFICATION,
(unsigned long)((uintptr_t)&tnotify));
if (ret < 0)
{
perror("TCIOC_NOTIFICATION");
goto errtimer;
}
/* If the timer is used only for measurement, start it here, otherwise
* start it only in one thread.
*/
if (config.wait_method != W_DEVTIMER)
{
ret = ioctl(timerfd, TCIOC_START);
if (ret < 0)
{
perror("TCIOC_START");
goto errtimer;
}
}
}
params = calloc(config.threads, sizeof(struct thread_param_s *));
if (params == NULL)
{
perror("params");
ret = ERROR;
goto main_error;
}
stats = calloc(config.threads, sizeof(struct thread_stats_s *));
if (stats == NULL)
{
perror("stats");
ret = ERROR;
goto main_error;
}
for (i = 0; i < config.threads; ++i)
{
params[i] = malloc(sizeof(struct thread_param_s));
if (params == NULL)
{
perror("params[i]");
ret = ERROR;
goto main_error;
}
stats[i] = malloc(sizeof(struct thread_stats_s));
if (params == NULL)
{
perror("stats[i]");
ret = ERROR;
goto main_error;
}
stats[i]->hist_array = calloc(config.histogram, sizeof(long));
if (stats[i]->hist_array == NULL)
{
perror("hist_array");
ret = ERROR;
goto main_error;
}
init_thread_param(params[i], config.interval, config.loops,
config.policy, config.prio, stats[i], config.clock);
pthread_create(&stats[i]->id, NULL, testthread, params[i]);
config.interval += config.distance;
if (config.prio > 1)
{
config.prio--;
}
}
while (running)
{
/* Periodically update the output */
usleep(100 * 1000);
int ended = 0;
for (i = 0; i < config.threads; ++i)
{
if (!config.quiet)
{
print_stat(params[i], i);
}
if (stats[i]->ended)
{
ended += 1;
}
}
if (ended == config.threads)
{
running = false;
}
else if (!config.quiet)
{
printf("\x1B[%dA", config.threads);
}
}
for (i = 0; i < config.threads; ++i)
{
pthread_join(stats[i]->id, NULL);
}
if (config.histogram)
{
print_hist(params, config.threads);
}
ret = OK;
if (config.wait_method == W_DEVTIMER || config.meas_method == M_TIMER_API)
{
ret = ioctl(timerfd, TCIOC_STOP);
if (ret < 0)
{
perror("TCIOC_STOP");
ret = ERROR;
}
close(timerfd);
}
main_error:
if (stats != NULL)
{
for (i = 0; i < config.threads; ++i)
{
if (params[i] != NULL)
{
free(params[i]);
}
if (stats[i] != NULL)
{
if (stats[i]->hist_array != NULL)
{
free(stats[i]->hist_array);
}
free(stats[i]);
}
}
}
free(stats);
return ret;
errtimer:
close(timerfd);
return ERROR;
}