forked from brainboxdotcc/DPP
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsocketengine.h
More file actions
332 lines (287 loc) · 8.78 KB
/
socketengine.h
File metadata and controls
332 lines (287 loc) · 8.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#pragma once
#include <dpp/export.h>
#include <dpp/socket.h>
#include <cstdint>
#include <unordered_map>
#include <memory>
#include <string_view>
#include <functional>
#include <shared_mutex>
#include <dpp/thread_pool.h>
namespace dpp {
/**
* @brief Types of IO events a socket may subscribe to.
*/
enum socket_event_flags : uint8_t {
/**
* @brief Socket wants to receive events when it can be read from.
* This is provided by the underlying implementation.
*/
WANT_READ = 1,
/**
* @brief Socket wants to receive events when it can be written to.
* This is provided by the underlying implementation, and will be
* a one-off event. If you want to receive ongoing write events you
* must re-request this event type each time.
*/
WANT_WRITE = 2,
/**
* @brief Socket wants to receive events that indicate an error condition.
* Note that EOF (graceful close) is not an error condition and is indicated
* by errno being 0 and ::read() returning 0.
*/
WANT_ERROR = 4,
/**
* @brief Socket should be removed as soon as is safe to do so. Generally, this is
* after the current iteration through the active event list.
*/
WANT_DELETION = 8,
};
/**
* @brief Read ready event
*/
using socket_read_event = std::function<void(dpp::socket fd, const struct socket_events&)>;
/**
* @brief Write ready event
*/
using socket_write_event = std::function<void(dpp::socket fd, const struct socket_events&)>;
/**
* @brief Error event
*/
using socket_error_event = std::function<void(dpp::socket fd, const struct socket_events&, int error_code)>;
/**
* @brief Contains statistics about the IO loop
*/
struct DPP_EXPORT socket_stats {
/**
* @brief Number of reads since startup
*/
uint64_t reads{0};
/**
* @brief Number of writes since startup
*/
uint64_t writes{0};
/**
* @brief Number of errors since startup
*/
uint64_t errors{0};
/**
* @brief Number of updates to file descriptors
*/
uint64_t updates{0};
/**
* @brief Number of deletions of file descriptors
*/
uint64_t deletions{0};
/**
* @brief Number of loop iterations since startup
*/
uint64_t iterations{0};
/**
* @brief Number of currently active file descriptors
*/
uint64_t active_fds{0};
/**
* @brief Socket engine type
*/
std::string_view engine_type;
};
/**
* @brief Represents an active socket event set in the socket engine.
*
* An event set contains a file descriptor, a set of event handler callbacks, and
* a set of bitmask flags which indicate which events it wants to receive.
* It is possible to quickly toggle event types on or off, as it is not always necessary
* or desired to receive all events all the time, in fact doing so can cause an event
* storm which will consume 100% CPU (e.g. if you request to receive write events all
* the time).
*/
struct DPP_EXPORT socket_events {
/**
* @brief File descriptor
*
* This should be a valid file descriptor created via ::socket().
*/
dpp::socket fd{INVALID_SOCKET};
/**
* @brief Flag bit mask of values from dpp::socket_event_flags
*/
uint8_t flags{0};
/**
* @brief Read ready event
* @note This function will be called from a different thread to that
* which adds the event set to the socket engine.
*/
socket_read_event on_read{};
/**
* @brief Write ready event
* @note This function will be called from a different thread to that
* which adds the event set to the socket engine.
*/
socket_write_event on_write{};
/**
* @brief Error event
* @note This function will be called from a different thread to that
* which adds the event set to the socket engine.
*/
socket_error_event on_error{};
/**
* @brief Construct a new socket_events
* @param socket_fd file descriptor
* @param _flags initial flags bitmask
* @param read_event read ready event
* @param write_event write ready event
* @param error_event error event
*/
socket_events(dpp::socket socket_fd, uint8_t _flags, const socket_read_event& read_event, const socket_write_event& write_event = {}, const socket_error_event& error_event = {})
: fd(socket_fd), flags(_flags), on_read(read_event), on_write(write_event), on_error(error_event) { }
/**
* @brief Default constructor
*/
socket_events() = default;
};
/**
* @brief Container of event sets keyed by socket file descriptor
*/
using socket_container = std::unordered_map<dpp::socket, std::unique_ptr<socket_events>>;
/**
* @brief This is the base class for socket engines.
* The actual implementation is OS specific and the correct implementation is detected by
* CMake. It is then compiled specifically into DPP so only one implementation can exist
* in the implementation. All implementations should behave identically to the user, abstracting
* out implementation-specific behaviours (e.g. difference between edge and level triggered
* event mechanisms etc).
*/
struct DPP_EXPORT socket_engine_base {
/**
* @brief Owning cluster
*/
class cluster* owner{nullptr};
/**
* @brief Default constructor
* @param creator Owning cluster
*/
socket_engine_base(class cluster* creator);
/**
* @brief Non-copyable
*/
socket_engine_base(const socket_engine_base&) = delete;
/**
* @brief Non-copyable
*/
socket_engine_base(socket_engine_base&&) = delete;
/**
* @brief Non-movable
*/
socket_engine_base& operator=(const socket_engine_base&) = delete;
/**
* @brief Non-movable
*/
socket_engine_base& operator=(socket_engine_base&&) = delete;
/**
* @brief Default destructor
*/
virtual ~socket_engine_base();
/**
* @brief Should be called repeatedly in a loop.
* Will run for a maximum of 1 second.
*/
virtual void process_events() = 0;
/**
* @brief Register a new socket with the socket engine
* @param e Socket events
* @return true if socket was added
*/
virtual bool register_socket(const socket_events& e);
/**
* @brief Update an existing socket in the socket engine
* @param e Socket events
* @return true if socket was updated
*/
virtual bool update_socket(const socket_events& e);
/**
* @brief Delete a socket from the socket engine
* @note This will not remove the socket immediately. It will set the
* WANT_DELETION flag causing it to be removed as soon as is safe to do so
* (once all events associated with it are completed).
* @param e File descriptor
* @return true if socket was queued for deletion
*/
bool delete_socket(dpp::socket fd);
/**
* @brief Iterate through the list of sockets and remove any
* with WANT_DELETION set. This will also call implementation-specific
* remove_socket() on each entry to be removed.
*/
void prune();
/**
* @brief Merge new flags in with the given file descriptor
* @param fd file descriptor
* @param extra_flags extra flags to add
*/
void inplace_modify_fd(dpp::socket fd, uint8_t extra_flags);
/**
* @brief Get statistics for socket engine
* @return socket stats
*/
const socket_stats& get_stats() const;
protected:
/**
* @brief Mutex for fds
*/
std::shared_mutex fds_mutex;
/**
* @brief File descriptors, and their states
*/
socket_container fds;
/**
* @brief Socket engine statistics
*/
socket_stats stats{};
/**
* @brief Find a file descriptors socket events
* @param fd file descriptor
* @return file descriptor or nullptr if doesn't exist
*/
socket_events* get_fd(dpp::socket fd);
/**
* @brief Called by the prune() function to remove sockets when safe to do so.
* This is normally at the end or before an iteration of the event loop.
* @param fd File descriptor to remove
*/
virtual bool remove_socket(dpp::socket fd);
};
/**
* @brief This is implemented by whatever derived form socket_engine takes
* @param creator Creating cluster
*/
DPP_EXPORT std::unique_ptr<socket_engine_base> create_socket_engine(class cluster *creator);
#ifndef _WIN32
/**
* @brief Set up a signal handler to be ignored
* @param signal Signal to set. If the signal is already set up with a handler,
* this will do nothing.
*/
void set_signal_handler(int signal);
#endif
};