/*
 *  stress_timer_handler()
 *	catch timer signal and cancel if no more runs flagged
 */
static void MLOCKED_TEXT stress_timer_handler(int sig)
{
	struct itimerspec timer;
	sigset_t mask;

	(void)sig;

	if (!stress_timer_keep_stressing())
		goto cancel;
	timer_counter++;

	if (sigpending(&mask) == 0)
		if (sigismember(&mask, SIGINT))
			goto cancel;
	/* High freq timer, check periodically for timeout */
	if ((timer_counter & 65535) == 0)
		if ((time_now() - start) > (double)g_opt_timeout)
			goto cancel;
	if (g_keep_stressing_flag) {
		int ret = timer_getoverrun(timerid);
		if (ret > 0)
			overruns += ret;
		stress_timer_set(&timer);
		return;
	}

cancel:
	g_keep_stressing_flag = false;
	/* Cancel timer if we detect no more runs */
	(void)memset(&timer, 0, sizeof(timer));
	(void)timer_settime(timerid, 0, &timer, NULL);
}
/*
 *  stress_timer
 *	stress timers
 */
int stress_timer(
	uint64_t *const counter,
	const uint32_t instance,
	const uint64_t max_ops,
	const char *name)
{
	struct sigaction new_action;
	struct sigevent sev;
	struct itimerspec timer;

	(void)instance;

	if (!set_timer_freq) {
		if (opt_flags & OPT_FLAGS_MAXIMIZE)
			opt_timer_freq = MAX_TIMER_FREQ;
		if (opt_flags & OPT_FLAGS_MINIMIZE)
			opt_timer_freq = MIN_TIMER_FREQ;
	}
	rate_ns = opt_timer_freq ? 1000000000 / opt_timer_freq : 1000000000;

	new_action.sa_flags = 0;
	new_action.sa_handler = stress_timer_handler;
	sigemptyset(&new_action.sa_mask);
	if (sigaction(SIGRTMIN, &new_action, NULL) < 0) {
		pr_failed_err(name, "sigaction");
		return EXIT_FAILURE;
	}

	sev.sigev_notify = SIGEV_SIGNAL;
	sev.sigev_signo = SIGRTMIN;
	sev.sigev_value.sival_ptr = &timerid;
	if (timer_create(CLOCK_REALTIME, &sev, &timerid) < 0) {
		pr_failed_err(name, "timer_create");
		return EXIT_FAILURE;
	}

	stress_timer_set(&timer);
	if (timer_settime(timerid, 0, &timer, NULL) < 0) {
		pr_failed_err(name, "timer_settime");
		return EXIT_FAILURE;
	}

	do {
		struct timespec req;

		req.tv_sec = 0;
		req.tv_nsec = 10000000;
		(void)nanosleep(&req, NULL);
		*counter = timer_counter;
	} while (opt_do_run && (!max_ops || timer_counter < max_ops));

	if (timer_delete(timerid) < 0) {
		pr_failed_err(name, "timer_delete");
		return EXIT_FAILURE;
	}
	pr_dbg(stderr, "%s: %" PRIu64 " timer overruns (instance %" PRIu32 ")\n",
		name, overruns, instance);

	return EXIT_SUCCESS;
}
/*
 *  stress_timer
 *	stress timers
 */
static int stress_timer(const args_t *args)
{
	struct sigevent sev;
	struct itimerspec timer;
	sigset_t mask;
	uint64_t timer_freq = DEFAULT_TIMER_FREQ;

	(void)sigemptyset(&mask);
	(void)sigaddset(&mask, SIGINT);
	(void)sigprocmask(SIG_SETMASK, &mask, NULL);

	max_ops = args->max_ops;
	start = time_now();

	if (!get_setting("timer-freq", &timer_freq)) {
		if (g_opt_flags & OPT_FLAGS_MAXIMIZE)
			timer_freq = MAX_TIMER_FREQ;
		if (g_opt_flags & OPT_FLAGS_MINIMIZE)
			timer_freq = MIN_TIMER_FREQ;
	}
	rate_ns = timer_freq ? 1000000000.0 / timer_freq : 1000000000.0;

	if (stress_sighandler(args->name, SIGRTMIN, stress_timer_handler, NULL) < 0)
		return EXIT_FAILURE;

	sev.sigev_notify = SIGEV_SIGNAL;
	sev.sigev_signo = SIGRTMIN;
	sev.sigev_value.sival_ptr = &timerid;
	if (timer_create(CLOCK_REALTIME, &sev, &timerid) < 0) {
		pr_fail_err("timer_create");
		return EXIT_FAILURE;
	}

	stress_timer_set(&timer);
	if (timer_settime(timerid, 0, &timer, NULL) < 0) {
		pr_fail_err("timer_settime");
		return EXIT_FAILURE;
	}

	do {
		struct timespec req;

		req.tv_sec = 0;
		req.tv_nsec = 10000000;
		(void)nanosleep(&req, NULL);
		set_counter(args, timer_counter);
	} while (keep_stressing());

	if (timer_delete(timerid) < 0) {
		pr_fail_err("timer_delete");
		return EXIT_FAILURE;
	}
	pr_dbg("%s: %" PRIu64 " timer overruns (instance %" PRIu32 ")\n",
		args->name, overruns, args->instance);

	return EXIT_SUCCESS;
}
/*
 *  stress_timer_handler()
 *	catch timer signal and cancel if no more runs flagged
 */
static void MLOCKED stress_timer_handler(int sig)
{
	struct itimerspec timer;
	int ret;

	(void)sig;
	timer_counter++;

	ret = timer_getoverrun(timerid);
	if (ret > 0)
		overruns += ret;

	if (opt_do_run) {
		stress_timer_set(&timer);
	} else {
		/* Cancel timer if we detect no more runs */

		timer.it_value.tv_sec = 0;
		timer.it_value.tv_nsec = 0;
		timer.it_interval.tv_sec = 0;
		timer.it_interval.tv_nsec = 0;
	}
	(void)timer_settime(timerid, 0, &timer, NULL);
}