/*
 * gcc -O2 -o signal-echo signal-echo.c 
 * ./signal-echo 1000000
 */
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/time.h>

static volatile sig_atomic_t signal_received = 0;

static void handle_signal(int signo)
{
	signal_received = 1;
}

int
main(int argc, char **argv)
{
	int	num_signals;
	int	parent_pid = getpid();
	int	child_pid;

	/* should get two arguments */
	if (argc != 2)
	{
		printf("invalid number of arguments\n");
		exit(1);
	}

	/* install simple signal handler */
	if (signal(SIGURG, handle_signal) == SIG_ERR)
	{
		printf("failed to set signal\n");
		exit(2);
	}

	num_signals = atoi(argv[1]);
	printf("nmm_signals = %d\n", num_signals);

	/* fork child, send ping signals, wait for response */
	if ((child_pid = fork()) != 0)
	{
		int cnt = 0;		/* number of signals sent */
		int waiting = 0;	/* are we waiting for response? */

		/* used to measure time */
		struct timeval time_start;
		struct timeval time_end;

		/* total duration (since time_start) */
		int duration;

		/* sleep a bit, so that child fully starts */
		usleep(10000);

		/* remember when we started */
		gettimeofday(&time_start, NULL);

		/* send num_signals pings */
		while (cnt < num_signals)
		{
			/* if not waiting for response, send a signal */
			if (waiting == 0)
			{
				waiting = 1;
				kill(child_pid, SIGURG);
			}

			/* have received response signal from child? */
			if (signal_received)
			{
				/* reset the flags */
				signal_received = 0;
				waiting = 0;

				/* account for the roundtrip */
				cnt++;

				/* print progress every 100k signals */
				if (cnt % 100000 == 0)
				{
					/* distance from time_start s*/
					gettimeofday(&time_end, NULL);
					duration = (time_end.tv_sec - time_start.tv_sec) * 1000000 +
							   (time_end.tv_usec - time_start.tv_usec);

					/* print summary up to now */
					printf("PROGRESS: sent %d signals in %d us (%.2f / sig) (%.0f sig / s)\n",
						   cnt, duration, (duration * 1.0 / cnt),
						   cnt * 1.0 / (duration / 1000000.0));
				}
			}
		}

		/* final summary */
		gettimeofday(&time_end, NULL);
		duration = (time_end.tv_sec - time_start.tv_sec) * 1000000 +
				   (time_end.tv_usec - time_start.tv_usec);

		printf("TOTAL: sent %d signals in %d us (%.2f us)\n",
			   cnt, duration, duration * 1.0 / cnt);

		printf("TOTAL: signals / sec = %.2f\n", cnt / (duration / 1000000.0));
	}
	else
	{
		int cnt = 0;		/* number of signals received/sent */

		/* wait for signal, send signal back */
		while (cnt < num_signals)
		{
			if (signal_received)
			{
				signal_received = 0;
				cnt++;

				kill(parent_pid, SIGURG);
			}
		}
	}

	return 0;
}
