big_pipe_test.c revision 291183
1#include <sys/select.h>
2#include <err.h>
3#include <errno.h>
4#include <fcntl.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <unistd.h>
9
10#define BIG_PIPE_SIZE  64*1024 /* From sys/pipe.h */
11
12/*
13 * Test for the non-blocking big pipe bug (write(2) returning
14 * EAGAIN while select(2) returns the descriptor as ready for write).
15 *
16 * $FreeBSD: stable/10/tests/sys/kern/pipe/big_pipe_test.c 290914 2015-11-16 05:38:40Z ngie $
17 */
18
19static void
20write_frame(int fd, char *buf, unsigned long buflen)
21{
22	fd_set wfd;
23	int i;
24
25	while (buflen) {
26		FD_ZERO(&wfd);
27		FD_SET(fd, &wfd);
28		i = select(fd+1, NULL, &wfd, NULL, NULL);
29		if (i < 0)
30			err(1, "select failed");
31		if (i != 1) {
32			errx(1, "select returned unexpected value %d\n", i);
33			exit(1);
34		}
35		i = write(fd, buf, buflen);
36		if (i < 0) {
37			if (errno != EAGAIN)
38				warn("write failed");
39			exit(1);
40		}
41		buf += i;
42		buflen -= i;
43	}
44}
45
46int
47main(void)
48{
49	/* any value over PIPE_SIZE should do */
50	char buf[BIG_PIPE_SIZE];
51	int i, flags, fd[2];
52
53	if (pipe(fd) < 0)
54		errx(1, "pipe failed");
55
56	flags = fcntl(fd[1], F_GETFL);
57	if (flags == -1 || fcntl(fd[1], F_SETFL, flags|O_NONBLOCK) == -1) {
58		printf("fcntl failed: %s\n", strerror(errno));
59		exit(1);
60	}
61
62	switch (fork()) {
63	case -1:
64		err(1, "fork failed: %s\n", strerror(errno));
65		break;
66	case 0:
67		close(fd[1]);
68		for (;;) {
69			/* Any small size should do */
70			i = read(fd[0], buf, 256);
71			if (i == 0)
72				break;
73			if (i < 0)
74				err(1, "read");
75		}
76		exit(0);
77	default:
78		break;
79	}
80
81	close(fd[0]);
82	memset(buf, 0, sizeof buf);
83	for (i = 0; i < 1000; i++)
84		write_frame(fd[1], buf, sizeof buf);
85
86	printf("ok\n");
87	exit(0);
88}
89