opal-up 0.0.4 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,18 +18,18 @@
18
18
  #ifndef UWS_TOPICTREE_H
19
19
  #define UWS_TOPICTREE_H
20
20
 
21
- #include <map>
22
- #include <list>
21
+ #include <functional>
23
22
  #include <iostream>
24
- #include <unordered_set>
25
- #include <utility>
23
+ #include <list>
24
+ #include <map>
26
25
  #include <memory>
27
- #include <unordered_map>
28
- #include <vector>
29
- #include <string_view>
30
- #include <functional>
31
26
  #include <set>
32
27
  #include <string>
28
+ #include <string_view>
29
+ #include <unordered_map>
30
+ #include <unordered_set>
31
+ #include <utility>
32
+ #include <vector>
33
33
 
34
34
  namespace uWS {
35
35
 
@@ -37,327 +37,332 @@ struct Subscriber;
37
37
 
38
38
  struct Topic : std::unordered_set<Subscriber *> {
39
39
 
40
- Topic(std::string_view topic) : name(topic) {
41
-
42
- }
40
+ Topic(std::string_view topic) : name(topic) {}
43
41
 
44
- std::string name;
42
+ std::string name;
45
43
  };
46
44
 
47
45
  struct Subscriber {
48
46
 
49
- template <typename, typename> friend struct TopicTree;
47
+ template <typename, typename> friend struct TopicTree;
50
48
 
51
49
  private:
52
- /* We use a factory */
53
- Subscriber() = default;
50
+ /* We use a factory */
51
+ Subscriber() = default;
54
52
 
55
- /* State of prev, next does not matter unless we are needsDrainage() since we are not in the list */
56
- Subscriber *prev, *next;
53
+ /* State of prev, next does not matter unless we are needsDrainage() since we
54
+ * are not in the list */
55
+ Subscriber *prev, *next;
57
56
 
58
- /* Any one subscriber can be part of at most 32 publishes before it needs a drain,
59
- * or whatever encoding of runs or whatever we might do in the future */
60
- uint16_t messageIndices[32];
57
+ /* Any one subscriber can be part of at most 32 publishes before it needs a
58
+ * drain, or whatever encoding of runs or whatever we might do in the future
59
+ */
60
+ uint16_t messageIndices[32];
61
61
 
62
- /* This one matters the most, if it is 0 we are not in the list of drainableSubscribers */
63
- unsigned char numMessageIndices = 0;
62
+ /* This one matters the most, if it is 0 we are not in the list of
63
+ * drainableSubscribers */
64
+ unsigned char numMessageIndices = 0;
64
65
 
65
66
  public:
67
+ /* We have a list of topics we subscribe to (read by WebSocket::iterateTopics)
68
+ */
69
+ std::set<Topic *> topics;
66
70
 
67
- /* We have a list of topics we subscribe to (read by WebSocket::iterateTopics) */
68
- std::set<Topic *> topics;
71
+ /* User data */
72
+ void *user;
69
73
 
70
- /* User data */
71
- void *user;
72
-
73
- bool needsDrainage() {
74
- return numMessageIndices;
75
- }
74
+ bool needsDrainage() { return numMessageIndices; }
76
75
  };
77
76
 
78
- template <typename T, typename B>
79
- struct TopicTree {
77
+ template <typename T, typename B> struct TopicTree {
80
78
 
81
- enum IteratorFlags {
82
- LAST = 1,
83
- FIRST = 2
84
- };
79
+ enum IteratorFlags { LAST = 1, FIRST = 2 };
85
80
 
86
- /* Whomever is iterating this topic is locked to not modify its own list */
87
- Subscriber *iteratingSubscriber = nullptr;
81
+ /* Whomever is iterating this topic is locked to not modify its own list */
82
+ Subscriber *iteratingSubscriber = nullptr;
88
83
 
89
84
  private:
90
-
91
- /* The drain callback must not publish, unsubscribe or subscribe.
92
- * It must only cork, uncork, send, write */
93
- std::function<bool(Subscriber *, T &, IteratorFlags)> cb;
94
-
95
- /* The topics */
96
- std::unordered_map<std::string_view, std::unique_ptr<Topic>> topics;
97
-
98
- /* List of subscribers that needs drainage */
99
- Subscriber *drainableSubscribers = nullptr;
100
-
101
- /* Palette of outgoing messages, up to 64k */
102
- std::vector<T> outgoingMessages;
103
-
104
- void checkIteratingSubscriber(Subscriber *s) {
105
- /* Notify user that they are doing something wrong here */
106
- if (iteratingSubscriber == s) {
107
- std::cerr << "Error: WebSocket must not subscribe or unsubscribe to topics while iterating its topics!" << std::endl;
108
- std::terminate();
109
- }
85
+ /* The drain callback must not publish, unsubscribe or subscribe.
86
+ * It must only cork, uncork, send, write */
87
+ std::function<bool(Subscriber *, T &, IteratorFlags)> cb;
88
+
89
+ /* The topics */
90
+ std::unordered_map<std::string_view, std::unique_ptr<Topic>> topics;
91
+
92
+ /* List of subscribers that needs drainage */
93
+ Subscriber *drainableSubscribers = nullptr;
94
+
95
+ /* Palette of outgoing messages, up to 64k */
96
+ std::vector<T> outgoingMessages;
97
+
98
+ void checkIteratingSubscriber(Subscriber *s) {
99
+ /* Notify user that they are doing something wrong here */
100
+ if (iteratingSubscriber == s) {
101
+ std::cerr << "Error: WebSocket must not subscribe or unsubscribe to "
102
+ "topics while iterating its topics!"
103
+ << std::endl;
104
+ std::terminate();
110
105
  }
111
-
112
- /* Warning: does NOT unlink from drainableSubscribers or modify next, prev. */
113
- void drainImpl(Subscriber *s) {
114
- /* Before we call cb we need to make sure this subscriber will not report needsDrainage()
115
- * since WebSocket::send will call drain from within the cb in that case.*/
116
- int numMessageIndices = s->numMessageIndices;
117
- s->numMessageIndices = 0;
118
-
119
- /* Then we emit cb */
120
- for (int i = 0; i < numMessageIndices; i++) {
121
- T &outgoingMessage = outgoingMessages[s->messageIndices[i]];
122
-
123
- int flags = (i == numMessageIndices - 1) ? LAST : 0;
124
-
125
- /* Returning true will stop drainage short (such as when backpressure is too high) */
126
- if (cb(s, outgoingMessage, (IteratorFlags)(flags | (i == 0 ? FIRST : 0)))) {
127
- break;
128
- }
129
- }
106
+ }
107
+
108
+ /* Warning: does NOT unlink from drainableSubscribers or modify next, prev. */
109
+ void drainImpl(Subscriber *s) {
110
+ /* Before we call cb we need to make sure this subscriber will not report
111
+ * needsDrainage() since WebSocket::send will call drain from within the cb
112
+ * in that case.*/
113
+ int numMessageIndices = s->numMessageIndices;
114
+ s->numMessageIndices = 0;
115
+
116
+ /* Then we emit cb */
117
+ for (int i = 0; i < numMessageIndices; i++) {
118
+ T &outgoingMessage = outgoingMessages[s->messageIndices[i]];
119
+
120
+ int flags = (i == numMessageIndices - 1) ? LAST : 0;
121
+
122
+ /* Returning true will stop drainage short (such as when backpressure is
123
+ * too high) */
124
+ if (cb(s, outgoingMessage,
125
+ (IteratorFlags)(flags | (i == 0 ? FIRST : 0)))) {
126
+ break;
127
+ }
130
128
  }
129
+ }
131
130
 
132
- void unlinkDrainableSubscriber(Subscriber *s) {
133
- if (s->prev) {
134
- s->prev->next = s->next;
135
- }
136
- if (s->next) {
137
- s->next->prev = s->prev;
138
- }
139
- /* If we are the head, then we also need to reset the head */
140
- if (drainableSubscribers == s) {
141
- drainableSubscribers = s->next;
142
- }
131
+ void unlinkDrainableSubscriber(Subscriber *s) {
132
+ if (s->prev) {
133
+ s->prev->next = s->next;
134
+ }
135
+ if (s->next) {
136
+ s->next->prev = s->prev;
137
+ }
138
+ /* If we are the head, then we also need to reset the head */
139
+ if (drainableSubscribers == s) {
140
+ drainableSubscribers = s->next;
143
141
  }
142
+ }
144
143
 
145
144
  public:
146
-
147
- TopicTree(std::function<bool(Subscriber *, T &, IteratorFlags)> cb) : cb(cb) {
148
-
145
+ TopicTree(std::function<bool(Subscriber *, T &, IteratorFlags)> cb)
146
+ : cb(cb) {}
147
+
148
+ /* Returns nullptr if not found */
149
+ Topic *lookupTopic(std::string_view topic) {
150
+ auto it = topics.find(topic);
151
+ if (it == topics.end()) {
152
+ return nullptr;
149
153
  }
150
-
151
- /* Returns nullptr if not found */
152
- Topic *lookupTopic(std::string_view topic) {
153
- auto it = topics.find(topic);
154
- if (it == topics.end()) {
155
- return nullptr;
156
- }
157
- return it->second.get();
154
+ return it->second.get();
155
+ }
156
+
157
+ /* Subscribe fails if we already are subscribed */
158
+ Topic *subscribe(Subscriber *s, std::string_view topic) {
159
+ /* Notify user that they are doing something wrong here */
160
+ checkIteratingSubscriber(s);
161
+
162
+ /* Lookup or create new topic */
163
+ Topic *topicPtr = lookupTopic(topic);
164
+ if (!topicPtr) {
165
+ Topic *newTopic = new Topic(topic);
166
+ topics.insert(
167
+ {std::string_view(newTopic->name.data(), newTopic->name.length()),
168
+ std::unique_ptr<Topic>(newTopic)});
169
+ topicPtr = newTopic;
158
170
  }
159
171
 
160
- /* Subscribe fails if we already are subscribed */
161
- Topic *subscribe(Subscriber *s, std::string_view topic) {
162
- /* Notify user that they are doing something wrong here */
163
- checkIteratingSubscriber(s);
164
-
165
- /* Lookup or create new topic */
166
- Topic *topicPtr = lookupTopic(topic);
167
- if (!topicPtr) {
168
- Topic *newTopic = new Topic(topic);
169
- topics.insert({std::string_view(newTopic->name.data(), newTopic->name.length()), std::unique_ptr<Topic>(newTopic)});
170
- topicPtr = newTopic;
171
- }
172
-
173
- /* Insert us in topic, insert topic in us */
174
- auto [it, inserted] = s->topics.insert(topicPtr);
175
- if (!inserted) {
176
- return nullptr;
177
- }
178
- topicPtr->insert(s);
172
+ /* Insert us in topic, insert topic in us */
173
+ auto [it, inserted] = s->topics.insert(topicPtr);
174
+ if (!inserted) {
175
+ return nullptr;
176
+ }
177
+ topicPtr->insert(s);
178
+
179
+ /* Success */
180
+ return topicPtr;
181
+ }
182
+
183
+ /* Returns ok, last, newCount */
184
+ std::tuple<bool, bool, int> unsubscribe(Subscriber *s,
185
+ std::string_view topic) {
186
+ /* Notify user that they are doing something wrong here */
187
+ checkIteratingSubscriber(s);
188
+
189
+ /* Lookup topic */
190
+ Topic *topicPtr = lookupTopic(topic);
191
+ if (!topicPtr) {
192
+ /* If the topic doesn't exist we are assumed to still be subscribers of
193
+ * something */
194
+ return {false, false, -1};
195
+ }
179
196
 
180
- /* Success */
181
- return topicPtr;
197
+ /* Erase from our list first */
198
+ if (s->topics.erase(topicPtr) == 0) {
199
+ return {false, false, -1};
182
200
  }
183
201
 
184
- /* Returns ok, last, newCount */
185
- std::tuple<bool, bool, int> unsubscribe(Subscriber *s, std::string_view topic) {
186
- /* Notify user that they are doing something wrong here */
187
- checkIteratingSubscriber(s);
202
+ /* Remove us from topic */
203
+ topicPtr->erase(s);
188
204
 
189
- /* Lookup topic */
190
- Topic *topicPtr = lookupTopic(topic);
191
- if (!topicPtr) {
192
- /* If the topic doesn't exist we are assumed to still be subscribers of something */
193
- return {false, false, -1};
194
- }
205
+ int newCount = topicPtr->size();
195
206
 
196
- /* Erase from our list first */
197
- if (s->topics.erase(topicPtr) == 0) {
198
- return {false, false, -1};
199
- }
207
+ /* If there is no subscriber to this topic, remove it */
208
+ if (!topicPtr->size()) {
209
+ /* Unique_ptr deletes the topic */
210
+ topics.erase(topic);
211
+ }
200
212
 
201
- /* Remove us from topic */
202
- topicPtr->erase(s);
213
+ /* If we don't hold any topics we are to be freed altogether */
214
+ return {true, s->topics.size() == 0, newCount};
215
+ }
203
216
 
204
- int newCount = topicPtr->size();
217
+ /* Factory function for creating a Subscriber */
218
+ Subscriber *createSubscriber() { return new Subscriber(); }
205
219
 
206
- /* If there is no subscriber to this topic, remove it */
207
- if (!topicPtr->size()) {
208
- /* Unique_ptr deletes the topic */
209
- topics.erase(topic);
210
- }
220
+ /* This is used to end a Subscriber, before freeing it */
221
+ void freeSubscriber(Subscriber *s) {
211
222
 
212
- /* If we don't hold any topics we are to be freed altogether */
213
- return {true, s->topics.size() == 0, newCount};
223
+ /* I guess we call this one even if we are not subscribers */
224
+ if (!s) {
225
+ return;
214
226
  }
215
227
 
216
- /* Factory function for creating a Subscriber */
217
- Subscriber *createSubscriber() {
218
- return new Subscriber();
228
+ /* For all topics, unsubscribe */
229
+ for (Topic *topicPtr : s->topics) {
230
+ /* If we are the last subscriber, simply remove the whole topic */
231
+ if (topicPtr->size() == 1) {
232
+ topics.erase(topicPtr->name);
233
+ } else {
234
+ /* Otherwise just remove us */
235
+ topicPtr->erase(s);
236
+ }
219
237
  }
220
238
 
221
- /* This is used to end a Subscriber, before freeing it */
222
- void freeSubscriber(Subscriber *s) {
223
-
224
- /* I guess we call this one even if we are not subscribers */
225
- if (!s) {
226
- return;
227
- }
239
+ /* We also need to unlink us */
240
+ if (s->needsDrainage()) {
241
+ unlinkDrainableSubscriber(s);
242
+ }
228
243
 
229
- /* For all topics, unsubscribe */
230
- for (Topic *topicPtr : s->topics) {
231
- /* If we are the last subscriber, simply remove the whole topic */
232
- if (topicPtr->size() == 1) {
233
- topics.erase(topicPtr->name);
234
- } else {
235
- /* Otherwise just remove us */
236
- topicPtr->erase(s);
237
- }
238
- }
244
+ delete s;
245
+ }
246
+
247
+ /* Mainly used by WebSocket::send to drain one socket before sending */
248
+ void drain(Subscriber *s) {
249
+ /* The list is undefined and cannot be touched unless needsDrainage(). */
250
+ if (s->needsDrainage()) {
251
+ /* This function differs from drainImpl by properly unlinking
252
+ * the subscriber from drainableSubscribers. drainImpl does not. */
253
+ unlinkDrainableSubscriber(s);
254
+
255
+ /* This one always resets needsDrainage before it calls any cb's.
256
+ * Otherwise we would stackoverflow when sending after publish but before
257
+ * drain. */
258
+ drainImpl(s);
259
+
260
+ /* If we drained last subscriber, also clear outgoingMessages */
261
+ if (!drainableSubscribers) {
262
+ outgoingMessages.clear();
263
+ }
264
+ }
265
+ }
266
+
267
+ /* Called everytime we call send, to drain published messages so to sync
268
+ * outgoing messages */
269
+ void drain() {
270
+ if (drainableSubscribers) {
271
+ /* Drain one socket a time */
272
+ for (Subscriber *s = drainableSubscribers; s; s = s->next) {
273
+ /* Instead of unlinking every single subscriber, we just leave the list
274
+ * undefined and reset drainableSubscribers ptr below. */
275
+ drainImpl(s);
276
+ }
277
+ /* Drain always clears drainableSubscribers and outgoingMessages */
278
+ drainableSubscribers = nullptr;
279
+ outgoingMessages.clear();
280
+ }
281
+ }
282
+
283
+ /* Big messages bypass all buffering and land directly in backpressure */
284
+ template <typename F>
285
+ bool publishBig(Subscriber *sender, std::string_view topic, B &&bigMessage,
286
+ F cb) {
287
+ /* Do we even have this topic? */
288
+ auto it = topics.find(topic);
289
+ if (it == topics.end()) {
290
+ return false;
291
+ }
239
292
 
240
- /* We also need to unlink us */
241
- if (s->needsDrainage()) {
242
- unlinkDrainableSubscriber(s);
243
- }
293
+ /* For all subscribers in topic */
294
+ for (Subscriber *s : *it->second) {
244
295
 
245
- delete s;
296
+ /* If we are sender then ignore us */
297
+ if (sender != s) {
298
+ cb(s, bigMessage);
299
+ }
246
300
  }
247
301
 
248
- /* Mainly used by WebSocket::send to drain one socket before sending */
249
- void drain(Subscriber *s) {
250
- /* The list is undefined and cannot be touched unless needsDrainage(). */
251
- if (s->needsDrainage()) {
252
- /* This function differs from drainImpl by properly unlinking
253
- * the subscriber from drainableSubscribers. drainImpl does not. */
254
- unlinkDrainableSubscriber(s);
255
-
256
- /* This one always resets needsDrainage before it calls any cb's.
257
- * Otherwise we would stackoverflow when sending after publish but before drain. */
258
- drainImpl(s);
259
-
260
- /* If we drained last subscriber, also clear outgoingMessages */
261
- if (!drainableSubscribers) {
262
- outgoingMessages.clear();
263
- }
264
- }
265
- }
302
+ return true;
303
+ }
266
304
 
267
- /* Called everytime we call send, to drain published messages so to sync outgoing messages */
268
- void drain() {
269
- if (drainableSubscribers) {
270
- /* Drain one socket a time */
271
- for (Subscriber *s = drainableSubscribers; s; s = s->next) {
272
- /* Instead of unlinking every single subscriber, we just leave the list undefined
273
- * and reset drainableSubscribers ptr below. */
274
- drainImpl(s);
275
- }
276
- /* Drain always clears drainableSubscribers and outgoingMessages */
277
- drainableSubscribers = nullptr;
278
- outgoingMessages.clear();
279
- }
305
+ /* Linear in number of affected subscribers */
306
+ bool publish(Subscriber *sender, std::string_view topic, T &&message) {
307
+ /* Do we even have this topic? */
308
+ auto it = topics.find(topic);
309
+ if (it == topics.end()) {
310
+ return false;
280
311
  }
281
312
 
282
- /* Big messages bypass all buffering and land directly in backpressure */
283
- template <typename F>
284
- bool publishBig(Subscriber *sender, std::string_view topic, B &&bigMessage, F cb) {
285
- /* Do we even have this topic? */
286
- auto it = topics.find(topic);
287
- if (it == topics.end()) {
288
- return false;
289
- }
290
-
291
- /* For all subscribers in topic */
292
- for (Subscriber *s : *it->second) {
313
+ /* If we have more than 65k messages we need to drain every socket. */
314
+ if (outgoingMessages.size() == UINT16_MAX) {
315
+ /* If there is a socket that is currently corked, this will be ugly as all
316
+ * sockets will drain to their own backpressure */
317
+ drain();
318
+ }
293
319
 
294
- /* If we are sender then ignore us */
295
- if (sender != s) {
296
- cb(s, bigMessage);
297
- }
298
- }
320
+ /* If nobody references this message, don't buffer it */
321
+ bool referencedMessage = false;
299
322
 
300
- return true;
301
- }
323
+ /* For all subscribers in topic */
324
+ for (Subscriber *s : *it->second) {
302
325
 
303
- /* Linear in number of affected subscribers */
304
- bool publish(Subscriber *sender, std::string_view topic, T &&message) {
305
- /* Do we even have this topic? */
306
- auto it = topics.find(topic);
307
- if (it == topics.end()) {
308
- return false;
309
- }
326
+ /* If we are sender then ignore us */
327
+ if (sender != s) {
310
328
 
311
- /* If we have more than 65k messages we need to drain every socket. */
312
- if (outgoingMessages.size() == UINT16_MAX) {
313
- /* If there is a socket that is currently corked, this will be ugly as all sockets will drain
314
- * to their own backpressure */
315
- drain();
316
- }
329
+ /* At least one subscriber wants this message */
330
+ referencedMessage = true;
317
331
 
318
- /* If nobody references this message, don't buffer it */
319
- bool referencedMessage = false;
320
-
321
- /* For all subscribers in topic */
322
- for (Subscriber *s : *it->second) {
323
-
324
- /* If we are sender then ignore us */
325
- if (sender != s) {
326
-
327
- /* At least one subscriber wants this message */
328
- referencedMessage = true;
329
-
330
- /* If we already have too many outgoing messages on this subscriber, drain it now */
331
- if (s->numMessageIndices == 32) {
332
- /* This one does not need to check needsDrainage here but still does. */
333
- drain(s);
334
- }
335
-
336
- /* Finally we can continue */
337
- s->messageIndices[s->numMessageIndices++] = (uint16_t)outgoingMessages.size();
338
- /* First message adds subscriber to list of drainable subscribers */
339
- if (s->numMessageIndices == 1) {
340
- /* Insert us in the head of drainable subscribers */
341
- s->next = drainableSubscribers;
342
- s->prev = nullptr;
343
- if (s->next) {
344
- s->next->prev = s;
345
- }
346
- drainableSubscribers = s;
347
- }
348
- }
332
+ /* If we already have too many outgoing messages on this subscriber,
333
+ * drain it now */
334
+ if (s->numMessageIndices == 32) {
335
+ /* This one does not need to check needsDrainage here but still does.
336
+ */
337
+ drain(s);
349
338
  }
350
339
 
351
- /* Push this message and return with success */
352
- if (referencedMessage) {
353
- outgoingMessages.emplace_back(message);
340
+ /* Finally we can continue */
341
+ s->messageIndices[s->numMessageIndices++] =
342
+ (uint16_t)outgoingMessages.size();
343
+ /* First message adds subscriber to list of drainable subscribers */
344
+ if (s->numMessageIndices == 1) {
345
+ /* Insert us in the head of drainable subscribers */
346
+ s->next = drainableSubscribers;
347
+ s->prev = nullptr;
348
+ if (s->next) {
349
+ s->next->prev = s;
350
+ }
351
+ drainableSubscribers = s;
354
352
  }
353
+ }
354
+ }
355
355
 
356
- /* Success if someone wants it */
357
- return referencedMessage;
356
+ /* Push this message and return with success */
357
+ if (referencedMessage) {
358
+ outgoingMessages.emplace_back(message);
358
359
  }
360
+
361
+ /* Success if someone wants it */
362
+ return referencedMessage;
363
+ }
359
364
  };
360
365
 
361
- }
366
+ } // namespace uWS
362
367
 
363
368
  #endif