#define _GNU_SOURCE 1 #include #include #include #include #include #include #include #include #include #define USE_DOUBLE_POLL 1 #define USE_TRY_NO_POLL 0 #define DIE() die(__LINE__) static void die(int line) { char *buf = NULL; asprintf(&buf, "%d:", line); if (buf) perror(buf); exit (EXIT_FAILURE); } #define EV_WRITE 1 #define EV_READ 2 #define EV_POLL_WRITE 3 #define EV_POLL_READ 4 #if USE_TRY_NO_POLL # define EV_AFTER_READ EV_WRITE # define EV_AFTER_WRITE EV_READ #else # define EV_AFTER_READ EV_POLL_WRITE # define EV_AFTER_WRITE EV_POLL_READ #endif #define EV_IS_WRITE(x) ((x) & 1) static int do_event(int fd, int event) { char buf[1]; int ret = 0; switch (event) { case EV_WRITE: if ((ret = write(fd, "a", 1)) == -1) { if (errno != EAGAIN) DIE(); event = EV_POLL_WRITE; } else event = EV_AFTER_WRITE; break; case EV_READ: if ((ret = read(fd, buf, 1)) == -1) { if (errno == EPIPE) exit (EXIT_SUCCESS); if (errno != EAGAIN) DIE(); event = EV_POLL_READ; } else event = EV_AFTER_READ; break; case EV_POLL_WRITE: { struct pollfd p; p.fd = fd; p.events = POLLOUT; p.revents = 0; if (!USE_DOUBLE_POLL || !(ret = poll(&p, 1, 0))) ret = poll(&p, 1, -1); if (ret != 1) DIE(); event = EV_WRITE; } break; case EV_POLL_READ: { struct pollfd p; p.fd = fd; p.events = POLLIN; p.revents = 0; if (!USE_DOUBLE_POLL || !(ret = poll(&p, 1, 0))) ret = poll(&p, 1, -1); if (ret != 1) DIE(); event = EV_READ; } break; } return (event); } static void go_parent(int fd) { int event = EV_WRITE; unsigned int count = 100000; while (count) { int write_event = EV_IS_WRITE(event); event = do_event(fd, event); if (!!write_event != !!EV_IS_WRITE(event)) --count; } } static int go_chld(int fd) { int event = EV_WRITE; while (1) event = do_event(fd, event); } int main(void) { int fds[2]; pid_t chld = 0; if (socketpair(PF_LOCAL, SOCK_STREAM, IPPROTO_IP, fds) == -1) DIE(); if ((chld = fork()) == -1) DIE(); if (chld) go_parent(fds[0]); else go_chld(fds[1]); exit (EXIT_SUCCESS); }