Seastar
High performance C++ framework for concurrent servers
io_request.hh
1/*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18/*
19 * Copyright 2020 ScyllaDB
20 */
21
22#pragma once
23
24#include <seastar/core/sstring.hh>
25#include <seastar/core/on_internal_error.hh>
26#include <cassert>
27#include <cstdint>
28#include <vector>
29#include <sys/types.h>
30#include <sys/socket.h>
31
32namespace seastar {
33extern logger io_log;
34
35namespace internal {
36
37class io_request {
38public:
39 enum class operation : char { read, readv, write, writev, fdatasync, recv, recvmsg, send, sendmsg, accept, connect, poll_add, poll_remove, cancel };
40private:
41 // the upper layers give us void pointers, but storing void pointers here is just
42 // dangerous. The constructors seem to be happy to convert other pointers to void*,
43 // even if they are marked as explicit, and then you end up losing approximately 3 hours
44 // and 15 minutes (hypothetically, of course), trying to chase the weirdest bug.
45 // Let's store a char* for safety, and cast it back to void* in the accessor.
46 struct read_op {
47 operation op;
48 bool nowait_works;
49 int fd;
50 uint64_t pos;
51 char* addr;
52 size_t size;
53 };
54 struct readv_op {
55 operation op;
56 bool nowait_works;
57 int fd;
58 uint64_t pos;
59 ::iovec* iovec;
60 size_t iov_len;
61 };
62 struct recv_op {
63 operation op;
64 int fd;
65 char* addr;
66 size_t size;
67 int flags;
68 };
69 struct recvmsg_op {
70 operation op;
71 int fd;
72 ::msghdr* msghdr;
73 int flags;
74 };
75 using send_op = recv_op;
76 using sendmsg_op = recvmsg_op;
77 using write_op = read_op;
78 using writev_op = readv_op;
79 struct fdatasync_op {
80 operation op;
81 int fd;
82 };
83 struct accept_op {
84 operation op;
85 int fd;
86 ::sockaddr* sockaddr;
87 socklen_t* socklen_ptr;
88 int flags;
89 };
90 struct connect_op {
91 operation op;
92 int fd;
93 ::sockaddr* sockaddr;
94 socklen_t socklen;
95 };
96 struct poll_add_op {
97 operation op;
98 int fd;
99 int events;
100 };
101 struct poll_remove_op {
102 operation op;
103 int fd;
104 char* addr;
105 };
106 struct cancel_op {
107 operation op;
108 int fd;
109 char* addr;
110 };
111
112 union {
113 read_op _read;
114 readv_op _readv;
115 recv_op _recv;
116 recvmsg_op _recvmsg;
117 send_op _send;
118 sendmsg_op _sendmsg;
119 write_op _write;
120 writev_op _writev;
121 fdatasync_op _fdatasync;
122 accept_op _accept;
123 connect_op _connect;
124 poll_add_op _poll_add;
125 poll_remove_op _poll_remove;
126 cancel_op _cancel;
127 };
128
129public:
130 static io_request make_read(int fd, uint64_t pos, void* address, size_t size, bool nowait_works) {
131 io_request req;
132 req._read = {
133 .op = operation::read,
134 .nowait_works = nowait_works,
135 .fd = fd,
136 .pos = pos,
137 .addr = reinterpret_cast<char*>(address),
138 .size = size,
139 };
140 return req;
141 }
142
143 static io_request make_readv(int fd, uint64_t pos, std::vector<iovec>& iov, bool nowait_works) {
144 io_request req;
145 req._readv = {
146 .op = operation::readv,
147 .nowait_works = nowait_works,
148 .fd = fd,
149 .pos = pos,
150 .iovec = iov.data(),
151 .iov_len = iov.size(),
152 };
153 return req;
154 }
155
156 static io_request make_recv(int fd, void* address, size_t size, int flags) {
157 io_request req;
158 req._recv = {
159 .op = operation::recv,
160 .fd = fd,
161 .addr = reinterpret_cast<char*>(address),
162 .size = size,
163 .flags = flags,
164 };
165 return req;
166 }
167
168 static io_request make_recvmsg(int fd, ::msghdr* msg, int flags) {
169 io_request req;
170 req._recvmsg = {
171 .op = operation::recvmsg,
172 .fd = fd,
173 .msghdr = msg,
174 .flags = flags,
175 };
176 return req;
177 }
178
179 static io_request make_send(int fd, const void* address, size_t size, int flags) {
180 io_request req;
181 req._send = {
182 .op = operation::send,
183 .fd = fd,
184 .addr = const_cast<char*>(reinterpret_cast<const char*>(address)),
185 .size = size,
186 .flags = flags,
187 };
188 return req;
189 }
190
191 static io_request make_sendmsg(int fd, ::msghdr* msg, int flags) {
192 io_request req;
193 req._sendmsg = {
194 .op = operation::sendmsg,
195 .fd = fd,
196 .msghdr = msg,
197 .flags = flags,
198 };
199 return req;
200 }
201
202 static io_request make_write(int fd, uint64_t pos, const void* address, size_t size, bool nowait_works) {
203 io_request req;
204 req._write = {
205 .op = operation::write,
206 .nowait_works = nowait_works,
207 .fd = fd,
208 .pos = pos,
209 .addr = const_cast<char*>(reinterpret_cast<const char*>(address)),
210 .size = size,
211 };
212 return req;
213 }
214
215 static io_request make_writev(int fd, uint64_t pos, std::vector<iovec>& iov, bool nowait_works) {
216 io_request req;
217 req._writev = {
218 .op = operation::writev,
219 .nowait_works = nowait_works,
220 .fd = fd,
221 .pos = pos,
222 .iovec = iov.data(),
223 .iov_len = iov.size(),
224 };
225 return req;
226 }
227
228 static io_request make_fdatasync(int fd) {
229 io_request req;
230 req._fdatasync = {
231 .op = operation::fdatasync,
232 .fd = fd,
233 };
234 return req;
235 }
236
237 static io_request make_accept(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) {
238 io_request req;
239 req._accept = {
240 .op = operation::accept,
241 .fd = fd,
242 .sockaddr = addr,
243 .socklen_ptr = addrlen,
244 .flags = flags,
245 };
246 return req;
247 }
248
249 static io_request make_connect(int fd, struct sockaddr* addr, socklen_t addrlen) {
250 io_request req;
251 req._connect = {
252 .op = operation::connect,
253 .fd = fd,
254 .sockaddr = addr,
255 .socklen = addrlen,
256 };
257 return req;
258 }
259
260 static io_request make_poll_add(int fd, int events) {
261 io_request req;
262 req._poll_add = {
263 .op = operation::poll_add,
264 .fd = fd,
265 .events = events,
266 };
267 return req;
268 }
269
270 static io_request make_poll_remove(int fd, void *addr) {
271 io_request req;
272 req._poll_remove = {
273 .op = operation::poll_remove,
274 .fd = fd,
275 .addr = reinterpret_cast<char*>(addr),
276 };
277 return req;
278 }
279
280 static io_request make_cancel(int fd, void *addr) {
281 io_request req;
282 req._cancel = {
283 .op = operation::cancel,
284 .fd = fd,
285 .addr = reinterpret_cast<char*>(addr),
286 };
287 return req;
288 }
289
290 bool is_read() const {
291 switch (opcode()) {
292 case operation::read:
293 case operation::readv:
294 case operation::recvmsg:
295 case operation::recv:
296 return true;
297 default:
298 return false;
299 }
300 }
301
302 bool is_write() const {
303 switch (opcode()) {
304 case operation::write:
305 case operation::writev:
306 case operation::send:
307 case operation::sendmsg:
308 return true;
309 default:
310 return false;
311 }
312 }
313
314 sstring opname() const;
315
316 // All operation variants are tagged unions with an operation as
317 // the first member, which we read through the _read union member
318 // (chosen arbitrarily) which is allowed by the common-initial-subsequence rule.
319 operation opcode() const {
320 return _read.op;
321 }
322
323 template <operation Op>
324 auto& as() const {
325 if constexpr (Op == operation::read) {
326 return _read;
327 }
328 if constexpr (Op == operation::readv) {
329 return _readv;
330 }
331 if constexpr (Op == operation::recv) {
332 return _recv;
333 }
334 if constexpr (Op == operation::recvmsg) {
335 return _recvmsg;
336 }
337 if constexpr (Op == operation::send) {
338 return _send;
339 }
340 if constexpr (Op == operation::sendmsg) {
341 return _sendmsg;
342 }
343 if constexpr (Op == operation::write) {
344 return _write;
345 }
346 if constexpr (Op == operation::writev) {
347 return _writev;
348 }
349 if constexpr (Op == operation::fdatasync) {
350 return _fdatasync;
351 }
352 if constexpr (Op == operation::accept) {
353 return _accept;
354 }
355 if constexpr (Op == operation::connect) {
356 return _connect;
357 }
358 if constexpr (Op == operation::poll_add) {
359 return _poll_add;
360 }
361 if constexpr (Op == operation::poll_remove) {
362 return _poll_remove;
363 }
364 if constexpr (Op == operation::cancel) {
365 return _cancel;
366 }
367 }
368
369 struct part;
370 std::vector<part> split(size_t max_length);
371
372private:
373 io_request sub_req_buffer(size_t pos, size_t len) const {
374 io_request sub_req;
375 // read_op and write_op share the same layout, so we don't handle
376 // them separately
377 auto& op = _read;
378 auto& sub_op = sub_req._read;
379 sub_op = {
380 .op = op.op,
381 .nowait_works = op.nowait_works,
382 .fd = op.fd,
383 .pos = op.pos + pos,
384 .addr = op.addr + pos,
385 .size = len,
386 };
387 return sub_req;
388 }
389 std::vector<part> split_buffer(size_t max_length);
390
391 io_request sub_req_iovec(size_t pos, std::vector<iovec>& iov) const {
392 io_request sub_req;
393 // readv_op and writev_op share the same layout, so we don't handle
394 // them separately
395 auto& op = _readv;
396 auto& sub_op = sub_req._readv;
397 sub_op = {
398 .op = op.op,
399 .nowait_works = op.nowait_works,
400 .fd = op.fd,
401 .pos = op.pos + pos,
402 .iovec = iov.data(),
403 .iov_len = iov.size(),
404 };
405 return sub_req;
406 }
407 std::vector<part> split_iovec(size_t max_length);
408};
409
410struct io_request::part {
411 io_request req;
412 size_t size;
413 std::vector<::iovec> iovecs;
414};
415
416// Helper pair of IO direction and length
417struct io_direction_and_length {
418 size_t _directed_length; // bit 0 is R/W flag
419
420public:
421 size_t length() const noexcept { return _directed_length >> 1; }
422 int rw_idx() const noexcept { return _directed_length & 0x1; }
423 static constexpr int read_idx = 1;
424 static constexpr int write_idx = 0;
425
426 io_direction_and_length(int idx, size_t val) noexcept
427 : _directed_length((val << 1) | idx)
428 {
429 assert(idx == read_idx || idx == write_idx);
430 }
431};
432
433}
434}
future< connected_socket > connect(socket_address sa)
Seastar API namespace.
Definition: abort_on_ebadf.hh:26