Add test case for thread exiting with pending IO
A thread exiting should not have its IO canceled, that should only happen
when the parent exits. Test that we are able to issue IO from a thread,
with the parent reaping the completion when the thread exits.
Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/test/Makefile b/test/Makefile
index 6aa1788..ce5d9e3 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -102,6 +102,7 @@
stdout \
submit-reuse \
teardowns \
+ thread-exit \
timeout \
timeout-new \
timeout-overflow \
@@ -226,6 +227,7 @@
stdout.c \
submit-reuse.c \
teardowns.c \
+ thread-exit.c \
timeout-new.c \
timeout-overflow.c \
timeout.c \
@@ -248,6 +250,7 @@
wakeup-hang: XCFLAGS = -lpthread
pipe-eof: XCFLAGS = -lpthread
timeout-new: XCFLAGS = -lpthread
+thread-exit: XCFLAGS = -lpthread
install: $(test_targets) runtests.sh runtests-loop.sh
$(INSTALL) -D -d -m 755 $(datadir)/liburing-test/
diff --git a/test/thread-exit.c b/test/thread-exit.c
new file mode 100644
index 0000000..722edbc
--- /dev/null
+++ b/test/thread-exit.c
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test that thread pool issued requests don't cancel on thread
+ * exit, but do get canceled once the parent exits. Do both
+ * writes that finish and a poll request that sticks around.
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/poll.h>
+#include <pthread.h>
+
+#include "liburing.h"
+
+#define NR_IOS 8
+#define WSIZE 512
+
+static int create_file(const char *file, size_t size)
+{
+ ssize_t ret;
+ char *buf;
+ int fd;
+
+ buf = malloc(size);
+ memset(buf, 0xaa, size);
+
+ fd = open(file, O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ perror("open file");
+ return 1;
+ }
+ ret = write(fd, buf, size);
+ close(fd);
+ free(buf);
+ return ret != size;
+}
+
+struct d {
+ int fd;
+ struct io_uring *ring;
+ unsigned long off;
+ int pipe_fd;
+ int err;
+};
+
+static void *do_io(void *data)
+{
+ struct d *d = data;
+ struct io_uring_sqe *sqe;
+ char *buffer;
+ int ret;
+
+ buffer = malloc(WSIZE);
+ memset(buffer, 0x5a, WSIZE);
+ sqe = io_uring_get_sqe(d->ring);
+ if (!sqe) {
+ d->err++;
+ return NULL;
+ }
+ io_uring_prep_write(sqe, d->fd, buffer, WSIZE, d->off);
+ sqe->user_data = d->off;
+
+ sqe = io_uring_get_sqe(d->ring);
+ if (!sqe) {
+ d->err++;
+ return NULL;
+ }
+ io_uring_prep_poll_add(sqe, d->pipe_fd, POLLIN);
+
+ ret = io_uring_submit(d->ring);
+ if (ret != 2)
+ d->err++;
+
+ free(buffer);
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ const char *fname;
+ pthread_t thread;
+ int ret, do_unlink, i, fd;
+ struct d d;
+ int fds[2];
+
+ if (pipe(fds) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ if (argc > 1) {
+ fname = argv[1];
+ do_unlink = 0;
+ } else {
+ fname = ".thread.exit";
+ do_unlink = 1;
+ }
+
+ if (do_unlink && create_file(fname, 4096)) {
+ fprintf(stderr, "file create failed\n");
+ return 1;
+ }
+
+ fd = open(fname, O_WRONLY);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ d.fd = fd;
+ d.ring = ˚
+ d.off = 0;
+ d.pipe_fd = fds[0];
+ d.err = 0;
+ for (i = 0; i < NR_IOS; i++) {
+ memset(&thread, 0, sizeof(thread));
+ pthread_create(&thread, NULL, do_io, &d);
+ pthread_join(thread, NULL);
+ d.off += WSIZE;
+ }
+
+ for (i = 0; i < NR_IOS; i++) {
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "io_uring_wait_cqe=%d\n", ret);
+ goto err;
+ }
+ if (cqe->res != WSIZE) {
+ fprintf(stderr, "cqe->res=%d, Expected %d\n", cqe->res,
+ WSIZE);
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ if (do_unlink)
+ unlink(fname);
+ return d.err;
+err:
+ if (do_unlink)
+ unlink(fname);
+ return 1;
+}