io.hpp 11.1 KB
Newer Older
1
2
3
4
5
6
7
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright © 2020 Florian Fischer
#pragma once

#include <sys/socket.h>
#include <sys/types.h>

8
#include <cassert>
9
#include <cstddef>
10
#include <functional>
11
#include <memory>
12
#include <string>
13

14
#include "io/Future.hpp"	// IWYU pragma: keep
15
16
#include "io/IoContext.hpp"

17
18
19
20
#ifndef NDEBUG
#include "Runtime.hpp"
#endif

21
22
23
24
25
26
/*
 * Header defining the public POSIX-like IO interface of emper.
 * All functions defined in this header must be used inside a fiber.
 * The emper IoContext to handle the IO request will be the one setup by the
 * Runtime and returned by IoContext::getCurrentIo().
 */
27

28
29
class Fiber;

30
namespace emper::io {
31
/**
32
33
 * @brief non-blocking recv mimicking POSIX recv(3)
 *
34
 * This method must be called from inside the emper runtime because it uses
35
 * the worker-local IoContext
36
 *
37
 * @param socket file descriptor for this operation
38
 * @param buffer destination buffer
39
 * @param length length of the message in bytes
Florian Fischer's avatar
Florian Fischer committed
40
 * @param flags type of message reception
41
42
 *
 * @return Future object which signals the completion of the recv request
43
 */
44
inline auto recv(int socket, void *buffer, size_t length, int flags) -> std::unique_ptr<Future> {
45
	assert(Runtime::inRuntime());
46
	IoContext *io = IoContext::getWorkerIo();
47
48
	return io->recv(socket, buffer, length, flags);
}
49

50
/**
51
52
 * @brief Blocking recv mimicking POSIX recv(3)
 *
53
 * This method must be called from inside the emper runtime because it uses
54
 * the worker-local IoContext
55
 *
56
57
58
 * @param socket file descriptor for this operation
 * @param buffer destination buffer
 * @param length length of the message in bytes
Florian Fischer's avatar
Florian Fischer committed
59
 * @param flags type of message reception
60
61

 * @return -1 on error, 0 when receiving from a closed socket, otherwise the received bytes
62
 */
63
inline auto recvAndWait(int socket, void *buffer, size_t length, int flags) -> ssize_t {
64
	assert(Runtime::inRuntime());
65
66
	IoContext *io = IoContext::getWorkerIo();
	return io->recvAndWait(socket, buffer, length, flags);
67
}
68

69
/**
70
71
 * @brief Non-blocking send mimicking POSIX send(3)
 *
72
 * This method must be called from inside the emper runtime because it uses
73
 * the worker-local IoContext
74
 *
75
 * @param socket file descriptor for this operation
76
 * @param buffer source buffer
77
 * @param length length of the message in bytes
Florian Fischer's avatar
Florian Fischer committed
78
 * @param flags type of message transmission
79
80
 *
 * @return Future object which signals the completion of the send request
81
 */
82
inline auto send(int socket, const void *buffer, size_t length, int flags)
83
		-> std::unique_ptr<Future> {
84
	assert(Runtime::inRuntime());
85
	IoContext *io = IoContext::getWorkerIo();
86
	return io->send(socket, buffer, length, flags);
87
}
88

89
/**
90
91
 * @brief Blocking send mimicking POSIX send(3)
 *
92
 * This method must be called from inside the emper runtime because it uses
93
 * the worker-local IoContext
94
 *
95
96
 * @param socket file descriptor for this operation
 * @param buffer source buffer
97
 * @param length length of the message in bytes
Florian Fischer's avatar
Florian Fischer committed
98
 * @param flags type of message transmission
99
100
 *
 * @return -1 on error, otherwise the number of sent bytes
101
 */
102
inline auto sendAndWait(int socket, const void *buffer, size_t length, int flags) -> ssize_t {
103
	assert(Runtime::inRuntime());
104
	IoContext *io = IoContext::getWorkerIo();
105
	return io->sendAndWait(socket, buffer, length, flags);
106
}
107

108
/**
109
110
111
112
113
114
115
116
 * @brief Non-blocking connect mimicking POSIX connect(3)
 *
 * It is discouraged to call connect on a socket with the SOCK_NONBLOCK flag set
 * because the request submitted to io_uring will immediately be completed
 * with EINPROGESS or EALREADY and must be repeatedly reinserted into the submission
 * queue and io_uring afterwards.
 * To efficiently connect a socket don't set its SOCK_NONBLOCK flag.
 *
117
 * This method must be called from inside the emper runtime because it uses
118
 * the worker-local IoContext
119
 *
120
121
122
123
124
 * @param socket file descriptor for this operation
 * @param address buffer containing the address to connect to
 * @param address_len length of the sockaddr structure
 *
 * @return Future object which signals the completion of the connect request
125
 */
126
inline auto connect(int socket, const struct sockaddr *address, socklen_t address_len)
127
		-> std::unique_ptr<Future> {
128
	assert(Runtime::inRuntime());
129
	IoContext *io = IoContext::getWorkerIo();
130
131
	return io->connect(socket, address, address_len);
}
132
133

/**
134
135
136
137
138
139
140
141
 * @brief Blocking connect mimicking POSIX connect(3)
 *
 * It is discouraged to call connect on a socket with the SOCK_NONBLOCK flag set
 * because the request submitted to io_uring will immediately be completed
 * with EINPROGESS or EALREADY and must be repeatedly reinserted into the submission
 * queue and io_uring afterwards.
 * To efficiently connect a socket don't set its SOCK_NONBLOCK flag.
 *
142
 * This method must be called from inside the emper runtime because it uses
143
 * the worker-local IoContext
144
 *
145
146
147
 * @param socket file descriptor for this operation
 * @param address buffer containing the address to connect to
 * @param address_len length of the sockaddr structure
148
149
 *
 * @return -1 on error, 0 otherwise
150
 */
151
inline auto connectAndWait(int socket, const struct sockaddr *address, socklen_t address_len)
152
		-> int {
153
	assert(Runtime::inRuntime());
154
155
	IoContext *io = IoContext::getWorkerIo();
	return io->connectAndWait(socket, address, address_len);
156
}
157

158
/**
159
160
161
162
163
164
165
166
 * @brief Non-blocking accept mimicking POSIX accept(3)
 *
 * It is discouraged to call accept on a socket with the SOCK_NONBLOCK flag set
 * because the request submitted to io_uring will immediately be completed
 * with EAGAIN or EWOULDBLOCK and must be repeatedly reinserted into the submission
 * queue and io_uring afterwards.
 * To efficiently accept new connections don't set the SOCK_NONBLOCK flag.
 *
167
 * If emper is build with the meson option 'io_try_syscall' after a new connection
168
169
170
 * is accepted the SOCK_NONBLOCK flag on the returned socket will be set to enable
 * future non-blocking recv or send syscalls.
 *
171
 * This method must be called from inside the emper runtime because it uses
172
 * the worker-local IoContext
173
 *
174
 * @param socket file descriptor for this operation
175
176
 * @param[out] address buffer to store the clients address
 * @param[in, out] address_len length of the supplied sockaddr structure
177
178
 *
 * @return Future object which signals the completion of the accept request
179
 */
180
inline auto accept(int socket, struct sockaddr *address, socklen_t *address_len)
181
		-> std::unique_ptr<Future> {
182
	assert(Runtime::inRuntime());
183
	IoContext *io = IoContext::getWorkerIo();
184
185
	return io->accept(socket, address, address_len);
}
186

187
/**
188
189
190
191
192
193
194
195
 * @brief Blocking accept mimicking POSIX accept(3)
 *
 * It is discouraged to call accept on a socket with the SOCK_NONBLOCK flag set
 * because the request submitted to io_uring will immediately be completed
 * with EAGAIN or EWOULDBLOCK and must be repeatedly reinserted into the submission
 * queue and io_uring afterwards.
 * To efficiently accept new connections don't set the SOCK_NONBLOCK flag.
 *
196
 * If emper is build with the meson option 'io_try_syscall' after a new connection
197
198
199
 * is accepted the SOCK_NONBLOCK flag on the returned socket will be set to enable
 * future non-blocking recv or send syscalls.
 *
200
 * This method must be called from inside the emper runtime because it uses
201
 * the worker-local IoContext
202
 *
203
 * @param socket file descriptor for this operation
204
205
 * @param[out] address buffer to store the clients address
 * @param[in, out] address_len length of the supplied sockaddr structure
206
207
 *
 * @return -1 on error, otherwise the non-negative file descriptor of the accepted socket.
208
 */
209
inline auto acceptAndWait(int socket, struct sockaddr *address, socklen_t *address_len) -> int {
210
	assert(Runtime::inRuntime());
211
212
	IoContext *io = IoContext::getWorkerIo();
	return io->acceptAndWait(socket, address, address_len);
213
}
214

215
/**
216
217
 * @brief Non-blocking read for regular files mimicking POSIX read(3)
 *
218
 * This method must be called from inside the emper runtime because it uses
219
 * the worker-local IoContext
220
 *
221
222
223
224
 * @param fildes file descriptor to the regular file to be read from
 * @param buf destination buffer
 * @param nbyte amount of bytes to read
 * @param offset offset in the file
225
226
 *
 * @return Future object which signals the completion of the read request
227
 */
228
inline auto readFile(int fildes, void *buf, size_t nbyte, off_t offset = 0)
229
		-> std::unique_ptr<Future> {
230
	assert(Runtime::inRuntime());
231
	IoContext *io = IoContext::getWorkerIo();
232
	return io->readFile(fildes, buf, nbyte, offset);
233
}
234
235

/**
236
237
 * @brief Blocking read for regular files mimicking POSIX read(3)
 *
238
 * This method must be called from inside the emper runtime because it uses
239
 * the worker-local IoContext
240
 *
241
 * @param fildes file descriptor to the regular file to be written to
242
 * @param buf destination buffer
243
 * @param nbyte amount of bytes to read
244
 * @param offset offset in the file
245
246
 *
 * @return -1 on error, otherwise the number of bytes read
247
 */
248
inline auto readFileAndWait(int fildes, void *buf, size_t nbyte, off_t offset = 0) -> ssize_t {
249
	assert(Runtime::inRuntime());
250
	IoContext *io = IoContext::getWorkerIo();
251
	return io->readFileAndWait(fildes, buf, nbyte, offset);
252
}
253
254

/**
255
256
257
258
259
 * @brief Non-blocking write for regular files mimicking POSIX write(3)
 *
 * Currently only reading from regular files is tested and supported.
 * Reading from other file types could work but may result in undefined behavior.
 *
260
 * This method must be called from inside the emper runtime because it uses
261
 * the worker-local IoContext
262
 *
263
 * @param fildes file descriptor to the regular file to be written to
264
265
266
 * @param buf source buffer
 * @param nbyte amount of bytes to write
 * @param offset offset in the file
267
268
 *
 * @return Future object which signals the completion of the write request
269
 */
270
271
inline auto writeFile(int fildes, const void *buf, size_t nbyte, off_t offset = 0)
		-> std::unique_ptr<Future> {
272
	assert(Runtime::inRuntime());
273
	IoContext *io = IoContext::getWorkerIo();
274
	return io->writeFile(fildes, buf, nbyte, offset);
275
}
276

277
/**
278
 * @brief Blocking write for regular files mimicking POSIX write(3)
279
280
281
282
 *
 * Currently only writing to regular files is tested and supported.
 * Writing to other file types could work but may result in undefined behavior.
 *
283
 * This method must be called from inside the emper runtime because it uses
284
 * the worker-local IoContext
285
 *
286
 * @param fildes file descriptor to the regular file to be written to
287
 * @param buf source buffer
288
 * @param nbyte amount of bytes to write
289
 * @param offset offset in the file
290
 *
291
 * @return -1 on error, otherwise the number of bytes written
292
 */
293
294
inline auto writeFileAndWait(int fildes, const void *buf, size_t nbyte, off_t offset = 0)
		-> ssize_t {
295
	assert(Runtime::inRuntime());
296
	IoContext *io = IoContext::getWorkerIo();
297
	return io->writeFileAndWait(fildes, buf, nbyte, offset);
298
}
299

300
301
302
303
304
305
306
307
308
309
310
/**
 * @brief Fiber accepting TCP connections and scheduling handler fibers
 *
 * @param host The host address the socket should be listening on
 * @param port The port the socket should be listening on
 * @param handler A function or lambda the started handler Fibers should execute.
 *        It is called with the file descriptor returned by accept.
 * @param backlog The TCP connection backlog.
 *
 * @return nullptr on error, otherwise the TCP listener Fiber
 */
311
312
313
auto tcp_listener(std::string &host, int &port, const std::function<void(int)> &handler,
									int backlog = 1024) -> Fiber *;

314
}	 // namespace emper::io