/*
 * DF-5793 directed probe: churner pipes into the LOWEST fd pair; spammer
 * dup2()s ONLY onto fd 3 (the read side of every pipe in a quiescent fd
 * table).  Counts dup2() calls that took >250ms (the 1s tsleep retry).
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <pthread.h>
#include <time.h>

static volatile int stop = 0;
static volatile long pipes_done = 0;
#define SRCFD 250

static double now_sec(void)
{
	struct timespec ts;
	clock_gettime(CLOCK_MONOTONIC, &ts);
	return ts.tv_sec + ts.tv_nsec / 1e9;
}

static void *churner(void *arg)
{
	int pfds[2];
	long n = 0;

	/* normalize: close everything above 2 */
	{
		int i;
		for (i = 3; i < 16; i++)
			close(i);
	}
	while (!stop) {
		if (pipe(pfds) == 0) {
			close(pfds[0]);
			close(pfds[1]);
			n++;
		}
	}
	pipes_done = n;
	return NULL;
}

static void *spammer(void *arg)
{
	long n = 0, slow = 0, err = 0;

	while (!stop) {
		double t0 = now_sec();
		if (dup2(SRCFD, 3) >= 0)
			close(3);
		else
			err++;
		if (now_sec() - t0 > 0.25)
			slow++;
		n++;
	}
	fprintf(stderr, "spammer: attempts=%ld slow=%ld err=%ld errno_last=%d(%s)\n", n, slow, err, errno, strerror(errno));
	return NULL;
}

int main(int argc, char **argv)
{
	double t0, runlen = 10.0;
	pthread_t ta, tb;

	if (argc > 1)
		runlen = atof(argv[1]);
	{
		int s = open("/dev/null", O_RDONLY);
		if (dup2(s, SRCFD) < 0) { perror("dup2 src"); return 1; }
		close(s);
	}
	t0 = now_sec();
	pthread_create(&ta, NULL, churner, NULL);
	pthread_create(&tb, NULL, spammer, NULL);
	while (now_sec() - t0 < runlen)
		usleep(100000);
	stop = 1;
	pthread_join(ta, NULL);
	pthread_join(tb, NULL);
	printf("directed: pipes=%ld\n", pipes_done);
	return 0;
}
