// User-mode smoke test for POSIX pipe, nonblocking, and poll semantics. #include #include #include #include #include #include #include #include static void print(const char* text) { (void)write(1, text, strlen(text)); } // Terminates the smoke test with one diagnostic. static void fail(const char* message) { print("posix_pipe_smoke: "); print(message); print("\n"); exit(1); } // Requires one descriptor to expose the expected immediate poll bits. static short poll_now(int fd, short events) { struct pollfd descriptor; descriptor.fd = fd; descriptor.events = events; descriptor.revents = 0; int result = poll(&descriptor, 1, 0); if (result < 0) fail("poll failed"); return descriptor.revents; } // Runs pipe lifetime, readiness, EOF, broken-pipe, and atomic-write checks. int main(void) { int descriptors[2]; if (pipe(descriptors) != 0) fail("pipe failed"); struct stat st; if (fstat(descriptors[0], &st) != 0 || !S_ISFIFO(st.st_mode)) fail("read endpoint is not FIFO"); if (fstat(descriptors[1], &st) != 0 || !S_ISFIFO(st.st_mode)) fail("write endpoint is not FIFO"); if (poll_now(descriptors[0], POLLIN) & POLLIN) fail("empty pipe reported readable"); if (!(poll_now(descriptors[1], POLLOUT) & POLLOUT)) fail("empty pipe write endpoint not writable"); if (write(descriptors[1], "abc", 3) != 3) fail("basic write failed"); if (!(poll_now(descriptors[0], POLLIN) & POLLIN)) fail("pipe data did not become readable"); char buffer[4] = {0, 0, 0, 0}; if (read(descriptors[0], buffer, 3) != 3 || strcmp(buffer, "abc") != 0) fail("basic read failed"); if (fcntl(descriptors[0], F_SETFL, O_NONBLOCK) != 0) fail("read F_SETFL O_NONBLOCK failed"); errno = 0; if (read(descriptors[0], buffer, 1) != -1 || errno != EAGAIN) fail("empty nonblocking read did not return EAGAIN"); int writerAlias = dup(descriptors[1]); if (writerAlias < 0) fail("writer dup failed"); if (close(descriptors[1]) != 0) fail("close original writer failed"); if (poll_now(descriptors[0], POLLIN | POLLHUP) & POLLHUP) fail("HUP published before final writer closed"); if (close(writerAlias) != 0) fail("close final writer failed"); short eofEvents = poll_now(descriptors[0], POLLIN | POLLHUP); if ((eofEvents & (POLLIN | POLLHUP)) != (POLLIN | POLLHUP)) fail("EOF readiness transition missing"); if (read(descriptors[0], buffer, 1) != 0) fail("final writer close did not produce EOF"); if (close(descriptors[0]) != 0) fail("close read endpoint failed"); if (pipe(descriptors) != 0) fail("second pipe failed"); if (close(descriptors[0]) != 0) fail("close second read endpoint failed"); sighandler_t previousPipe = signal(SIGPIPE, SIG_IGN); if (previousPipe == SIG_ERR) fail("ignore SIGPIPE failed"); errno = 0; if (write(descriptors[1], "x", 1) != -1 || errno != EPIPE) fail("write without readers did not return EPIPE"); if (signal(SIGPIPE, previousPipe) == SIG_ERR) fail("restore SIGPIPE failed"); if (close(descriptors[1]) != 0) fail("close broken writer failed"); if (pipe(descriptors) != 0) fail("atomic pipe failed"); if (fcntl(descriptors[1], F_SETFL, O_NONBLOCK) != 0) fail("write F_SETFL O_NONBLOCK failed"); char atomic[PIPE_BUF]; memset(atomic, 'A', sizeof(atomic)); for (;;) { errno = 0; ssize_t written = write(descriptors[1], atomic, sizeof(atomic)); if (written == (ssize_t)sizeof(atomic)) continue; if (written == -1 && errno == EAGAIN) break; fail("PIPE_BUF write was partially committed"); } if (poll_now(descriptors[1], POLLOUT) & POLLOUT) fail("full pipe still reported writable"); if (!(poll_now(descriptors[0], POLLIN) & POLLIN)) fail("full pipe read endpoint not readable"); if (close(descriptors[0]) != 0 || close(descriptors[1]) != 0) fail("atomic pipe close failed"); print("posix_pipe_smoke: PASS\n"); return 0; }