
#include <inc/lib.h>

void
umain(void)
{
	int p[2], r, i;
	struct Fd *fd;
	volatile struct Env *kid;

	cprintf("testing for pipeisclosed race...\n");
	if ((r = pipe(p)) < 0)
		panic("pipe: %e", r);
	if ((r = fork()) < 0)
		panic("fork: %e", r);
	if (r == 0) {
		// child just dups and closes repeatedly,
		// yielding so the parent can see
		// the fd state between the two.
		close(p[1]);
		for (i = 0; i < 200; i++) {
			if (i % 10 == 0)
				cprintf("%d.", i);
			// dup, then close.  yield so that other guy will
			// see us while we're between them.
			dup(p[0], 10);
			sys_yield();
			close(10);
			sys_yield();
		}
		exit();
	}

	// We hold both p[0] and p[1] open, so pipeisclosed should
	// never return false.
	// 
	// Now the ref count for p[0] will toggle between 2 and 3
	// as the child dups and closes it.
	// The ref count for p[1] is 1.
	// Thus the ref count for the underlying pipe structure 
	// will toggle between 3 and 4.
	//
	// If pipeisclosed checks pageref(p[0]) and gets 3, and
	// then the child closes, and then pipeisclosed checks
	// pageref(pipe structure) and gets 3, then it will return true
	// when it shouldn't.
	//
	// If pipeisclosed checks pageref(pipe structure) and gets 3,
	// and then the child dups, and then pipeisclosed checks
	// pageref(p[0]) and gets 3, then it will return true when
	// it shouldn't.
	//
	// So either way, pipeisclosed is going give a wrong answer.
	//
	kid = &envs[ENVX(r)];
	while (kid->env_status == ENV_RUNNABLE)
		if (pipeisclosed(p[0]) != 0) {
			cprintf("\nRACE: pipe appears closed\n");
			sys_env_destroy(r);
			exit();
		}
	cprintf("child done with loop\n");
	if (pipeisclosed(p[0]))
		panic("somehow the other end of p[0] got closed!");
	if ((r = fd_lookup(p[0], &fd)) < 0)
		panic("cannot look up p[0]: %e", r);
	(void) fd2data(fd);
	cprintf("race didn't happen\n");
}
