1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Check that a readv on a nonblocking socket queued before a writev doesn't
4 * wait for data to arrive.
5 */
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <assert.h>
10
11 #include <errno.h>
12 #include <fcntl.h>
13 #include <unistd.h>
14 #include <sys/socket.h>
15 #include <sys/un.h>
16 #include <netinet/tcp.h>
17 #include <netinet/in.h>
18
19 #include "liburing.h"
20
main(int argc,char * argv[])21 int main(int argc, char *argv[])
22 {
23 int p_fd[2], ret;
24 int32_t recv_s0;
25 int32_t val = 1;
26 struct sockaddr_in addr;
27
28 if (argc > 1)
29 return 0;
30
31 srand(getpid());
32
33 recv_s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
34
35 ret = setsockopt(recv_s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
36 assert(ret != -1);
37 ret = setsockopt(recv_s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
38 assert(ret != -1);
39
40 addr.sin_family = AF_INET;
41 addr.sin_addr.s_addr = 0x0100007fU;
42
43 do {
44 addr.sin_port = (rand() % 61440) + 4096;
45 ret = bind(recv_s0, (struct sockaddr*)&addr, sizeof(addr));
46 if (!ret)
47 break;
48 if (errno != EADDRINUSE) {
49 perror("bind");
50 exit(1);
51 }
52 } while (1);
53
54 ret = listen(recv_s0, 128);
55 assert(ret != -1);
56
57 p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
58
59 val = 1;
60 ret = setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
61 assert(ret != -1);
62
63 int32_t flags = fcntl(p_fd[1], F_GETFL, 0);
64 assert(flags != -1);
65
66 flags |= O_NONBLOCK;
67 ret = fcntl(p_fd[1], F_SETFL, flags);
68 assert(ret != -1);
69
70 ret = connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr));
71 assert(ret == -1);
72
73 p_fd[0] = accept(recv_s0, NULL, NULL);
74 assert(p_fd[0] != -1);
75
76 flags = fcntl(p_fd[0], F_GETFL, 0);
77 assert(flags != -1);
78
79 flags |= O_NONBLOCK;
80 ret = fcntl(p_fd[0], F_SETFL, flags);
81 assert(ret != -1);
82
83 while (1) {
84 int32_t code;
85 socklen_t code_len = sizeof(code);
86
87 ret = getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len);
88 assert(ret != -1);
89
90 if (!code)
91 break;
92 }
93
94 struct io_uring m_io_uring;
95
96 ret = io_uring_queue_init(32, &m_io_uring, 0);
97 assert(ret >= 0);
98
99 char recv_buff[128];
100 char send_buff[128];
101
102 {
103 struct iovec iov[1];
104
105 iov[0].iov_base = recv_buff;
106 iov[0].iov_len = sizeof(recv_buff);
107
108 struct io_uring_sqe* sqe = io_uring_get_sqe(&m_io_uring);
109 assert(sqe != NULL);
110
111 io_uring_prep_readv(sqe, p_fd[0], iov, 1, 0);
112 sqe->user_data = 1;
113 }
114
115 {
116 struct iovec iov[1];
117
118 iov[0].iov_base = send_buff;
119 iov[0].iov_len = sizeof(send_buff);
120
121 struct io_uring_sqe* sqe = io_uring_get_sqe(&m_io_uring);
122 assert(sqe != NULL);
123
124 io_uring_prep_writev(sqe, p_fd[1], iov, 1, 0);
125 sqe->user_data = 2;
126 }
127
128 ret = io_uring_submit_and_wait(&m_io_uring, 2);
129 assert(ret != -1);
130
131 struct io_uring_cqe* cqe;
132 uint32_t head;
133 uint32_t count = 0;
134
135 while (count != 2) {
136 io_uring_for_each_cqe(&m_io_uring, head, cqe) {
137 if (cqe->user_data == 2 && cqe->res != 128) {
138 fprintf(stderr, "write=%d\n", cqe->res);
139 goto err;
140 } else if (cqe->user_data == 1 && cqe->res != -EAGAIN) {
141 fprintf(stderr, "read=%d\n", cqe->res);
142 goto err;
143 }
144 count++;
145 }
146
147 assert(count <= 2);
148 io_uring_cq_advance(&m_io_uring, count);
149 }
150
151 io_uring_queue_exit(&m_io_uring);
152 return 0;
153 err:
154 io_uring_queue_exit(&m_io_uring);
155 return 1;
156 }
157