aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/liburing/test/waitid.c
diff options
context:
space:
mode:
authorthegeorg <thegeorg@yandex-team.com>2024-06-09 11:55:21 +0300
committerthegeorg <thegeorg@yandex-team.com>2024-06-09 12:07:55 +0300
commitafd4899380eea1c70e2a68714b5da1c9919ccdbd (patch)
treecd5120708784139bc6a0f8881da1ed8389a065b3 /contrib/libs/liburing/test/waitid.c
parenta83bd2dd3c21e38c6c0807ec5e679497ab567f24 (diff)
downloadydb-afd4899380eea1c70e2a68714b5da1c9919ccdbd.tar.gz
Update contrib/libs/liburing to 2.6
3b51a9fb14de805208d11f1c077c78bb5d487e0f
Diffstat (limited to 'contrib/libs/liburing/test/waitid.c')
-rw-r--r--contrib/libs/liburing/test/waitid.c374
1 files changed, 374 insertions, 0 deletions
diff --git a/contrib/libs/liburing/test/waitid.c b/contrib/libs/liburing/test/waitid.c
new file mode 100644
index 0000000000..f7ebe55ac7
--- /dev/null
+++ b/contrib/libs/liburing/test/waitid.c
@@ -0,0 +1,374 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test waitid functionality
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static bool no_waitid;
+
+static void child(long usleep_time)
+{
+ if (usleep_time)
+ usleep(usleep_time);
+ exit(0);
+}
+
+/*
+ * Test linked timeout with child not exiting in time
+ */
+static int test_noexit(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ siginfo_t si;
+ pid_t pid;
+ int ret, i;
+
+ pid = fork();
+ if (!pid) {
+ child(200000);
+ exit(0);
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_waitid(sqe, P_PID, pid, &si, WEXITED, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = 100 * 1000 * 1000ULL;
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 2;
+
+ io_uring_submit(ring);
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "cqe wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data == 2 && cqe->res != 1) {
+ fprintf(stderr, "timeout res: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data == 1 && cqe->res != -ECANCELED) {
+ fprintf(stderr, "waitid res: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return T_EXIT_PASS;
+}
+
+/*
+ * Test one child exiting, but not the one we were looking for
+ */
+static int test_double(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ siginfo_t si;
+ pid_t p1, p2;
+ int ret;
+
+ /* p1 will exit shortly */
+ p1 = fork();
+ if (!p1) {
+ child(100000);
+ exit(0);
+ }
+
+ /* p2 will linger */
+ p2 = fork();
+ if (!p2) {
+ child(200000);
+ exit(0);
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_waitid(sqe, P_PID, p2, &si, WEXITED, 0);
+
+ io_uring_submit(ring);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "cqe wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "cqe res: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ if (si.si_pid != p2) {
+ fprintf(stderr, "expected pid %d, got %d\n", p2, si.si_pid);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_PASS;
+}
+
+/*
+ * Test reaping of an already exited task
+ */
+static int test_ready(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ siginfo_t si;
+ pid_t pid;
+ int ret;
+
+ pid = fork();
+ if (!pid) {
+ child(0);
+ exit(0);
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_waitid(sqe, P_PID, pid, &si, WEXITED, 0);
+
+ io_uring_submit(ring);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "cqe wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "cqe res: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ if (si.si_pid != pid) {
+ fprintf(stderr, "expected pid %d, got %d\n", pid, si.si_pid);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_PASS;
+}
+
+/*
+ * Test cancelation of pending waitid
+ */
+static int test_cancel(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i;
+ pid_t pid;
+
+ pid = fork();
+ if (!pid) {
+ child(20000);
+ exit(0);
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_waitid(sqe, P_PID, pid, NULL, WEXITED, 0);
+ sqe->user_data = 1;
+
+ io_uring_submit(ring);
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_cancel64(sqe, 1, 0);
+ sqe->user_data = 2;
+
+ io_uring_submit(ring);
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "cqe wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data == 1 && cqe->res != -ECANCELED) {
+ fprintf(stderr, "cqe res: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data == 2 && cqe->res != 1) {
+ fprintf(stderr, "cqe res: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return T_EXIT_PASS;
+}
+
+/*
+ * Test cancelation of pending waitid, with expected races that either
+ * waitid trigger or cancelation will win.
+ */
+static int test_cancel_race(struct io_uring *ring, int async)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i;
+ pid_t pid;
+
+ for (i = 0; i < 10; i++) {
+ pid = fork();
+ if (!pid) {
+ child(getpid() & 1);
+ exit(0);
+ }
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_waitid(sqe, P_ALL, -1, NULL, WEXITED, 0);
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+ sqe->user_data = 1;
+
+ io_uring_submit(ring);
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_cancel64(sqe, 1, 0);
+ sqe->user_data = 2;
+
+ usleep(1);
+
+ io_uring_submit(ring);
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "cqe wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data == 1 && !(cqe->res == -ECANCELED ||
+ cqe->res == 0)) {
+ fprintf(stderr, "cqe1 res: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data == 2 &&
+ !(cqe->res == 1 || cqe->res == 0 || cqe->res == -ENOENT ||
+ cqe->res == -EALREADY)) {
+ fprintf(stderr, "cqe2 res: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return T_EXIT_PASS;
+}
+
+/*
+ * Test basic reap of child exit
+ */
+static int test(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ siginfo_t si;
+ pid_t pid;
+ int ret;
+
+ pid = fork();
+ if (!pid) {
+ child(100);
+ exit(0);
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_waitid(sqe, P_PID, pid, &si, WEXITED, 0);
+
+ io_uring_submit(ring);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "cqe wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* no waitid support */
+ if (cqe->res == -EINVAL) {
+ no_waitid = true;
+ return T_EXIT_SKIP;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "cqe res: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ if (si.si_pid != pid) {
+ fprintf(stderr, "expected pid %d, got %d\n", pid, si.si_pid);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return T_EXIT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret, i;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ io_uring_queue_init(8, &ring, 0);
+
+ ret = test(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test failed\n");
+ return T_EXIT_FAIL;
+ }
+ if (no_waitid)
+ return T_EXIT_SKIP;
+
+ ret = test_noexit(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_noexit failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_noexit(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_noexit failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_double(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_double failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_ready(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_ready failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_cancel(&ring);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_cancel failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ ret = test_cancel_race(&ring, i & 1);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test_cancel_race failed\n");
+ return T_EXIT_FAIL;
+ }
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}