summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2019-11-11 12:51:42 -0700
committerJens Axboe <axboe@kernel.dk>2019-11-11 12:51:42 -0700
commitda1ce8ce2fbbc9d4320a9d4593c35a3b90b83d58 (patch)
treec9ee27bf0d767f54dc28b69fd8906d68c0fad8da
parentd379852b802c8e225e0c06f002ac78be73b81279 (diff)
downloadliburing-da1ce8ce2fbbc9d4320a9d4593c35a3b90b83d58.tar.gz
liburing-da1ce8ce2fbbc9d4320a9d4593c35a3b90b83d58.tar.bz2
test/accept-link: add test case for accept linked with timers
Heavily based on a test case from Hrvoje Zeba <zeba.hrvoje@gmail.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--test/Makefile6
-rw-r--r--test/accept-link.c195
2 files changed, 199 insertions, 2 deletions
diff --git a/test/Makefile b/test/Makefile
index d381ce4..c1e6ca2 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -10,7 +10,7 @@ all_targets += poll poll-cancel ring-leak fsync io_uring_setup io_uring_register
cq-size 8a9973408177-test a0908ae19763-test 232c93d07b74-test \
socket-rw accept timeout-overflow defer read-write io-cancel \
link-timeout cq-overflow link_drain fc2a85cb02ef-test \
- poll-link
+ poll-link accept-link
include ../Makefile.quiet
@@ -27,7 +27,8 @@ test_srcs := poll.c poll-cancel.c ring-leak.c fsync.c io_uring_setup.c \
cq-peek-batch.c file-register.c cq-size.c 8a9973408177-test.c \
a0908ae19763-test.c 232c93d07b74-test.c socket-rw.c accept.c \
timeout-overflow.c defer.c read-write.c io-cancel.c link-timeout.c \
- cq-overflow.c link_drain.c fc2a85cb02ef-test.c poll-link.c
+ cq-overflow.c link_drain.c fc2a85cb02ef-test.c poll-link.c \
+ accept-link.c
test_objs := $(patsubst %.c,%.ol,$(test_srcs))
@@ -35,6 +36,7 @@ test_objs := $(patsubst %.c,%.ol,$(test_srcs))
232c93d07b74-test: XCFLAGS = -lpthread
send_recvmsg: XCFLAGS = -lpthread
poll-link: XCFLAGS = -lpthread
+accept-link: XCFLAGS = -lpthread
clean:
rm -f $(all_targets) $(test_objs)
diff --git a/test/accept-link.c b/test/accept-link.c
new file mode 100644
index 0000000..849ded0
--- /dev/null
+++ b/test/accept-link.c
@@ -0,0 +1,195 @@
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <poll.h>
+
+#include <liburing.h>
+
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+
+static int recv_thread_ready = 0;
+static int recv_thread_done = 0;
+
+static void signal_var(int *var)
+{
+ pthread_mutex_lock(&mutex);
+ *var = 1;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mutex);
+}
+
+static void wait_for_var(int *var)
+{
+ pthread_mutex_lock(&mutex);
+
+ while (!*var)
+ pthread_cond_wait(&cond, &mutex);
+
+ pthread_mutex_unlock(&mutex);
+}
+
+static void *send_thread(void *arg)
+{
+ wait_for_var(&recv_thread_ready);
+
+ int s0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ assert(s0 != -1);
+
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0x1235;
+ addr.sin_addr.s_addr = 0x0100007fU;
+
+ assert(connect(s0, (struct sockaddr*)&addr, sizeof(addr)) != -1);
+
+ wait_for_var(&recv_thread_done);
+
+ close(s0);
+ return 0;
+}
+
+struct data {
+ unsigned expected[2];
+ unsigned just_positive[2];
+ unsigned long timeout;
+};
+
+void *recv_thread(void *arg)
+{
+ struct data *data = arg;
+
+ struct io_uring ring;
+ assert(io_uring_queue_init(8, &ring, 0) == 0);
+
+ int s0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ assert(s0 != -1);
+
+ int32_t val = 1;
+ assert(setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) != -1);
+ assert(setsockopt(s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != -1);
+
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = 0x1235;
+ addr.sin_addr.s_addr = 0x0100007fU;
+
+ assert(bind(s0, (struct sockaddr*)&addr, sizeof(addr)) != -1);
+ assert(listen(s0, 128) != -1);
+
+ signal_var(&recv_thread_ready);
+
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(&ring);
+ assert(sqe != NULL);
+
+ io_uring_prep_accept(sqe, s0, NULL, NULL, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring);
+ assert(sqe != NULL);
+
+ struct __kernel_timespec ts;
+ ts.tv_sec = data->timeout / 1000000000;
+ ts.tv_nsec = data->timeout % 1000000000;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 2;
+
+ assert(io_uring_submit(&ring) == 2);
+
+ for (int i = 0; i < 2; i++) {
+ struct io_uring_cqe *cqe;
+ int idx;
+
+ if (io_uring_wait_cqe(&ring, &cqe)) {
+ fprintf(stderr, "wait cqe failed\n");
+ goto err;
+ }
+ idx = cqe->user_data - 1;
+ if (cqe->res != data->expected[idx]) {
+ if (cqe->res > 0 && data->just_positive[idx])
+ goto ok;
+ fprintf(stderr, "cqe got %d, wanted %d\n", cqe->res,
+ data->expected[idx]);
+ goto err;
+ }
+ok:
+ if (cqe->user_data == 1 && cqe->res > 0)
+ close(cqe->res);
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ signal_var(&recv_thread_done);
+
+ close(s0);
+ return NULL;
+err:
+ close(s0);
+ return (void *) 1;
+}
+
+static int test_accept_timeout(int do_connect, unsigned long timeout)
+{
+ pthread_t t1, t2;
+ struct data d;
+ void *tret;
+ int ret = 0;
+
+ recv_thread_ready = 0;
+ recv_thread_done = 0;
+
+ d.timeout = timeout;
+ if (!do_connect) {
+ d.expected[0] = -EINTR;
+ d.expected[1] = -EALREADY;
+ } else {
+ d.expected[0] = -1U;
+ d.just_positive[0] = 1;
+ d.expected[1] = -ECANCELED;
+ }
+
+ pthread_create(&t1, NULL, recv_thread, &d);
+
+ if (do_connect)
+ pthread_create(&t2, NULL, send_thread, NULL);
+
+ pthread_join(t1, &tret);
+ if (tret)
+ ret++;
+
+ if (do_connect) {
+ pthread_join(t2, &tret);
+ if (tret)
+ ret++;
+ }
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ if (test_accept_timeout(0, 1000000000)) {
+ fprintf(stderr, "accept timeout 0 failed\n");
+ return 1;
+ }
+
+ if (test_accept_timeout(1, 1000000000)) {
+ fprintf(stderr, "accept timeout 0 failed\n");
+ return 1;
+ }
+
+ return 0;
+}