/src/uWebSockets/src/TopicTree.h
Line | Count | Source |
1 | | /* |
2 | | * Authored by Alex Hultman, 2018-2021. |
3 | | * Intellectual property of third-party. |
4 | | |
5 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
6 | | * you may not use this file except in compliance with the License. |
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, software |
12 | | * distributed under the License is distributed on an "AS IS" BASIS, |
13 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
14 | | * See the License for the specific language governing permissions and |
15 | | * limitations under the License. |
16 | | */ |
17 | | |
18 | | #ifndef UWS_TOPICTREE_H |
19 | | #define UWS_TOPICTREE_H |
20 | | |
21 | | #include <map> |
22 | | #include <list> |
23 | | #include <iostream> |
24 | | #include <unordered_set> |
25 | | #include <utility> |
26 | | #include <memory> |
27 | | #include <unordered_map> |
28 | | #include <vector> |
29 | | #include <string_view> |
30 | | #include <functional> |
31 | | #include <set> |
32 | | #include <string> |
33 | | #include <exception> |
34 | | |
35 | | namespace uWS { |
36 | | |
37 | | struct Subscriber; |
38 | | |
39 | | struct Topic : std::unordered_set<Subscriber *> { |
40 | | |
41 | 6.76M | Topic(std::string_view topic) : name(topic) { |
42 | | |
43 | 6.76M | } |
44 | | |
45 | | std::string name; |
46 | | }; |
47 | | |
48 | | struct Subscriber { |
49 | | |
50 | | template <typename, typename> friend struct TopicTree; |
51 | | |
52 | | private: |
53 | | /* We use a factory */ |
54 | 2.33M | Subscriber() = default; |
55 | | |
56 | | /* State of prev, next does not matter unless we are needsDrainage() since we are not in the list */ |
57 | | Subscriber *prev, *next; |
58 | | |
59 | | /* Any one subscriber can be part of at most 32 publishes before it needs a drain, |
60 | | * or whatever encoding of runs or whatever we might do in the future */ |
61 | | uint16_t messageIndices[32]; |
62 | | |
63 | | /* This one matters the most, if it is 0 we are not in the list of drainableSubscribers */ |
64 | | unsigned char numMessageIndices = 0; |
65 | | |
66 | | public: |
67 | | |
68 | | /* We have a list of topics we subscribe to (read by WebSocket::iterateTopics) */ |
69 | | std::set<Topic *> topics; |
70 | | |
71 | | /* User data */ |
72 | | void *user; |
73 | | |
74 | 2.85M | bool needsDrainage() { |
75 | 2.85M | return numMessageIndices; |
76 | 2.85M | } |
77 | | }; |
78 | | |
79 | | template <typename T, typename B> |
80 | | struct TopicTree { |
81 | | |
82 | | enum IteratorFlags { |
83 | | LAST = 1, |
84 | | FIRST = 2 |
85 | | }; |
86 | | |
87 | | /* Whomever is iterating this topic is locked to not modify its own list */ |
88 | | Subscriber *iteratingSubscriber = nullptr; |
89 | | |
90 | | private: |
91 | | |
92 | | /* The drain callback must not publish, unsubscribe or subscribe. |
93 | | * It must only cork, uncork, send, write */ |
94 | | std::function<bool(Subscriber *, T &, IteratorFlags)> cb; |
95 | | |
96 | | /* The topics */ |
97 | | std::unordered_map<std::string_view, std::unique_ptr<Topic>> topics; |
98 | | |
99 | | /* List of subscribers that needs drainage */ |
100 | | Subscriber *drainableSubscribers = nullptr; |
101 | | |
102 | | /* Palette of outgoing messages, up to 64k */ |
103 | | std::vector<T> outgoingMessages; |
104 | | |
105 | 8.92M | void checkIteratingSubscriber(Subscriber *s) { |
106 | | /* Notify user that they are doing something wrong here */ |
107 | 8.92M | if (iteratingSubscriber == s) { |
108 | 0 | std::cerr << "Error: WebSocket must not subscribe or unsubscribe to topics while iterating its topics!" << std::endl; |
109 | 0 | std::terminate(); |
110 | 0 | } |
111 | 8.92M | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::checkIteratingSubscriber(uWS::Subscriber*) Line | Count | Source | 105 | 2.27M | void checkIteratingSubscriber(Subscriber *s) { | 106 | | /* Notify user that they are doing something wrong here */ | 107 | 2.27M | if (iteratingSubscriber == s) { | 108 | 0 | std::cerr << "Error: WebSocket must not subscribe or unsubscribe to topics while iterating its topics!" << std::endl; | 109 | 0 | std::terminate(); | 110 | 0 | } | 111 | 2.27M | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::checkIteratingSubscriber(uWS::Subscriber*) Line | Count | Source | 105 | 6.64M | void checkIteratingSubscriber(Subscriber *s) { | 106 | | /* Notify user that they are doing something wrong here */ | 107 | 6.64M | if (iteratingSubscriber == s) { | 108 | 0 | std::cerr << "Error: WebSocket must not subscribe or unsubscribe to topics while iterating its topics!" << std::endl; | 109 | 0 | std::terminate(); | 110 | 0 | } | 111 | 6.64M | } |
|
112 | | |
113 | | /* Warning: does NOT unlink from drainableSubscribers or modify next, prev. */ |
114 | 214k | void drainImpl(Subscriber *s) { |
115 | | /* Before we call cb we need to make sure this subscriber will not report needsDrainage() |
116 | | * since WebSocket::send will call drain from within the cb in that case.*/ |
117 | 214k | int numMessageIndices = s->numMessageIndices; |
118 | 214k | s->numMessageIndices = 0; |
119 | | |
120 | | /* Then we emit cb */ |
121 | 4.61M | for (int i = 0; i < numMessageIndices; i++) { |
122 | 4.40M | T &outgoingMessage = outgoingMessages[s->messageIndices[i]]; |
123 | | |
124 | 4.40M | int flags = (i == numMessageIndices - 1) ? LAST : 0; |
125 | | |
126 | | /* Returning true will stop drainage short (such as when backpressure is too high) */ |
127 | 4.40M | if (cb(s, outgoingMessage, (IteratorFlags)(flags | (i == 0 ? FIRST : 0)))) { |
128 | 2.02k | break; |
129 | 2.02k | } |
130 | 4.40M | } |
131 | 214k | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::drainImpl(uWS::Subscriber*) Line | Count | Source | 114 | 184k | void drainImpl(Subscriber *s) { | 115 | | /* Before we call cb we need to make sure this subscriber will not report needsDrainage() | 116 | | * since WebSocket::send will call drain from within the cb in that case.*/ | 117 | 184k | int numMessageIndices = s->numMessageIndices; | 118 | 184k | s->numMessageIndices = 0; | 119 | | | 120 | | /* Then we emit cb */ | 121 | 4.21M | for (int i = 0; i < numMessageIndices; i++) { | 122 | 4.03M | T &outgoingMessage = outgoingMessages[s->messageIndices[i]]; | 123 | | | 124 | 4.03M | int flags = (i == numMessageIndices - 1) ? LAST : 0; | 125 | | | 126 | | /* Returning true will stop drainage short (such as when backpressure is too high) */ | 127 | 4.03M | if (cb(s, outgoingMessage, (IteratorFlags)(flags | (i == 0 ? FIRST : 0)))) { | 128 | 958 | break; | 129 | 958 | } | 130 | 4.03M | } | 131 | 184k | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::drainImpl(uWS::Subscriber*) Line | Count | Source | 114 | 29.9k | void drainImpl(Subscriber *s) { | 115 | | /* Before we call cb we need to make sure this subscriber will not report needsDrainage() | 116 | | * since WebSocket::send will call drain from within the cb in that case.*/ | 117 | 29.9k | int numMessageIndices = s->numMessageIndices; | 118 | 29.9k | s->numMessageIndices = 0; | 119 | | | 120 | | /* Then we emit cb */ | 121 | 405k | for (int i = 0; i < numMessageIndices; i++) { | 122 | 376k | T &outgoingMessage = outgoingMessages[s->messageIndices[i]]; | 123 | | | 124 | 376k | int flags = (i == numMessageIndices - 1) ? LAST : 0; | 125 | | | 126 | | /* Returning true will stop drainage short (such as when backpressure is too high) */ | 127 | 376k | if (cb(s, outgoingMessage, (IteratorFlags)(flags | (i == 0 ? FIRST : 0)))) { | 128 | 1.06k | break; | 129 | 1.06k | } | 130 | 376k | } | 131 | 29.9k | } |
|
132 | | |
133 | 286k | void unlinkDrainableSubscriber(Subscriber *s) { |
134 | 286k | if (s->prev) { |
135 | 168k | s->prev->next = s->next; |
136 | 168k | } |
137 | 286k | if (s->next) { |
138 | 120k | s->next->prev = s->prev; |
139 | 120k | } |
140 | | /* If we are the head, then we also need to reset the head */ |
141 | 286k | if (drainableSubscribers == s) { |
142 | 117k | drainableSubscribers = s->next; |
143 | 117k | } |
144 | 286k | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::unlinkDrainableSubscriber(uWS::Subscriber*) Line | Count | Source | 133 | 183k | void unlinkDrainableSubscriber(Subscriber *s) { | 134 | 183k | if (s->prev) { | 135 | 109k | s->prev->next = s->next; | 136 | 109k | } | 137 | 183k | if (s->next) { | 138 | 84.2k | s->next->prev = s->prev; | 139 | 84.2k | } | 140 | | /* If we are the head, then we also need to reset the head */ | 141 | 183k | if (drainableSubscribers == s) { | 142 | 73.6k | drainableSubscribers = s->next; | 143 | 73.6k | } | 144 | 183k | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::unlinkDrainableSubscriber(uWS::Subscriber*) Line | Count | Source | 133 | 102k | void unlinkDrainableSubscriber(Subscriber *s) { | 134 | 102k | if (s->prev) { | 135 | 58.5k | s->prev->next = s->next; | 136 | 58.5k | } | 137 | 102k | if (s->next) { | 138 | 35.8k | s->next->prev = s->prev; | 139 | 35.8k | } | 140 | | /* If we are the head, then we also need to reset the head */ | 141 | 102k | if (drainableSubscribers == s) { | 142 | 44.2k | drainableSubscribers = s->next; | 143 | 44.2k | } | 144 | 102k | } |
|
145 | | |
146 | | public: |
147 | | |
148 | 26.4k | TopicTree(std::function<bool(Subscriber *, T &, IteratorFlags)> cb) : cb(cb) { |
149 | | |
150 | 26.4k | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::TopicTree(std::__1::function<bool (uWS::Subscriber*, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::IteratorFlags)>) Line | Count | Source | 148 | 3.11k | TopicTree(std::function<bool(Subscriber *, T &, IteratorFlags)> cb) : cb(cb) { | 149 | | | 150 | 3.11k | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::TopicTree(std::__1::function<bool (uWS::Subscriber*, uWS::TopicTreeMessage&, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::IteratorFlags)>) Line | Count | Source | 148 | 23.3k | TopicTree(std::function<bool(Subscriber *, T &, IteratorFlags)> cb) : cb(cb) { | 149 | | | 150 | 23.3k | } |
|
151 | | |
152 | | /* Returns nullptr if not found */ |
153 | 14.8M | Topic *lookupTopic(std::string_view topic) { |
154 | 14.8M | auto it = topics.find(topic); |
155 | 14.8M | if (it == topics.end()) { |
156 | 7.24M | return nullptr; |
157 | 7.24M | } |
158 | 7.56M | return it->second.get(); |
159 | 14.8M | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::lookupTopic(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 153 | 8.16M | Topic *lookupTopic(std::string_view topic) { | 154 | 8.16M | auto it = topics.find(topic); | 155 | 8.16M | if (it == topics.end()) { | 156 | 737k | return nullptr; | 157 | 737k | } | 158 | 7.42M | return it->second.get(); | 159 | 8.16M | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::lookupTopic(std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 153 | 6.64M | Topic *lookupTopic(std::string_view topic) { | 154 | 6.64M | auto it = topics.find(topic); | 155 | 6.64M | if (it == topics.end()) { | 156 | 6.51M | return nullptr; | 157 | 6.51M | } | 158 | 135k | return it->second.get(); | 159 | 6.64M | } |
|
160 | | |
161 | | /* Subscribe fails if we already are subscribed */ |
162 | 8.87M | Topic *subscribe(Subscriber *s, std::string_view topic) { |
163 | | /* Notify user that they are doing something wrong here */ |
164 | 8.87M | checkIteratingSubscriber(s); |
165 | | |
166 | | /* Lookup or create new topic */ |
167 | 8.87M | Topic *topicPtr = lookupTopic(topic); |
168 | 8.87M | if (!topicPtr) { |
169 | 6.76M | Topic *newTopic = new Topic(topic); |
170 | 6.76M | topics.insert({std::string_view(newTopic->name.data(), newTopic->name.length()), std::unique_ptr<Topic>(newTopic)}); |
171 | 6.76M | topicPtr = newTopic; |
172 | 6.76M | } |
173 | | |
174 | | /* Insert us in topic, insert topic in us */ |
175 | 8.87M | auto [it, inserted] = s->topics.insert(topicPtr); |
176 | 8.87M | if (!inserted) { |
177 | 59.0k | return nullptr; |
178 | 59.0k | } |
179 | 8.81M | topicPtr->insert(s); |
180 | | |
181 | | /* Success */ |
182 | 8.81M | return topicPtr; |
183 | 8.87M | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::subscribe(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 162 | 2.22M | Topic *subscribe(Subscriber *s, std::string_view topic) { | 163 | | /* Notify user that they are doing something wrong here */ | 164 | 2.22M | checkIteratingSubscriber(s); | 165 | | | 166 | | /* Lookup or create new topic */ | 167 | 2.22M | Topic *topicPtr = lookupTopic(topic); | 168 | 2.22M | if (!topicPtr) { | 169 | 249k | Topic *newTopic = new Topic(topic); | 170 | 249k | topics.insert({std::string_view(newTopic->name.data(), newTopic->name.length()), std::unique_ptr<Topic>(newTopic)}); | 171 | 249k | topicPtr = newTopic; | 172 | 249k | } | 173 | | | 174 | | /* Insert us in topic, insert topic in us */ | 175 | 2.22M | auto [it, inserted] = s->topics.insert(topicPtr); | 176 | 2.22M | if (!inserted) { | 177 | 59.0k | return nullptr; | 178 | 59.0k | } | 179 | 2.16M | topicPtr->insert(s); | 180 | | | 181 | | /* Success */ | 182 | 2.16M | return topicPtr; | 183 | 2.22M | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::subscribe(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 162 | 6.64M | Topic *subscribe(Subscriber *s, std::string_view topic) { | 163 | | /* Notify user that they are doing something wrong here */ | 164 | 6.64M | checkIteratingSubscriber(s); | 165 | | | 166 | | /* Lookup or create new topic */ | 167 | 6.64M | Topic *topicPtr = lookupTopic(topic); | 168 | 6.64M | if (!topicPtr) { | 169 | 6.51M | Topic *newTopic = new Topic(topic); | 170 | 6.51M | topics.insert({std::string_view(newTopic->name.data(), newTopic->name.length()), std::unique_ptr<Topic>(newTopic)}); | 171 | 6.51M | topicPtr = newTopic; | 172 | 6.51M | } | 173 | | | 174 | | /* Insert us in topic, insert topic in us */ | 175 | 6.64M | auto [it, inserted] = s->topics.insert(topicPtr); | 176 | 6.64M | if (!inserted) { | 177 | 0 | return nullptr; | 178 | 0 | } | 179 | 6.64M | topicPtr->insert(s); | 180 | | | 181 | | /* Success */ | 182 | 6.64M | return topicPtr; | 183 | 6.64M | } |
|
184 | | |
185 | | /* Returns ok, last, newCount */ |
186 | 49.9k | std::tuple<bool, bool, int> unsubscribe(Subscriber *s, std::string_view topic) { |
187 | | /* Notify user that they are doing something wrong here */ |
188 | 49.9k | checkIteratingSubscriber(s); |
189 | | |
190 | | /* Lookup topic */ |
191 | 49.9k | Topic *topicPtr = lookupTopic(topic); |
192 | 49.9k | if (!topicPtr) { |
193 | | /* If the topic doesn't exist we are assumed to still be subscribers of something */ |
194 | 1.69k | return {false, false, -1}; |
195 | 1.69k | } |
196 | | |
197 | | /* Erase from our list first */ |
198 | 48.2k | if (s->topics.erase(topicPtr) == 0) { |
199 | 1.68k | return {false, false, -1}; |
200 | 1.68k | } |
201 | | |
202 | | /* Remove us from topic */ |
203 | 46.5k | topicPtr->erase(s); |
204 | | |
205 | 46.5k | int newCount = (int) topicPtr->size(); |
206 | | |
207 | | /* If there is no subscriber to this topic, remove it */ |
208 | 46.5k | if (!topicPtr->size()) { |
209 | | /* Unique_ptr deletes the topic */ |
210 | 22.9k | topics.erase(topic); |
211 | 22.9k | } |
212 | | |
213 | | /* If we don't hold any topics we are to be freed altogether */ |
214 | 46.5k | return {true, s->topics.size() == 0, newCount}; |
215 | 48.2k | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::unsubscribe(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 186 | 49.3k | std::tuple<bool, bool, int> unsubscribe(Subscriber *s, std::string_view topic) { | 187 | | /* Notify user that they are doing something wrong here */ | 188 | 49.3k | checkIteratingSubscriber(s); | 189 | | | 190 | | /* Lookup topic */ | 191 | 49.3k | Topic *topicPtr = lookupTopic(topic); | 192 | 49.3k | if (!topicPtr) { | 193 | | /* If the topic doesn't exist we are assumed to still be subscribers of something */ | 194 | 1.04k | return {false, false, -1}; | 195 | 1.04k | } | 196 | | | 197 | | /* Erase from our list first */ | 198 | 48.2k | if (s->topics.erase(topicPtr) == 0) { | 199 | 1.68k | return {false, false, -1}; | 200 | 1.68k | } | 201 | | | 202 | | /* Remove us from topic */ | 203 | 46.5k | topicPtr->erase(s); | 204 | | | 205 | 46.5k | int newCount = (int) topicPtr->size(); | 206 | | | 207 | | /* If there is no subscriber to this topic, remove it */ | 208 | 46.5k | if (!topicPtr->size()) { | 209 | | /* Unique_ptr deletes the topic */ | 210 | 22.9k | topics.erase(topic); | 211 | 22.9k | } | 212 | | | 213 | | /* If we don't hold any topics we are to be freed altogether */ | 214 | 46.5k | return {true, s->topics.size() == 0, newCount}; | 215 | 48.2k | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::unsubscribe(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >) Line | Count | Source | 186 | 649 | std::tuple<bool, bool, int> unsubscribe(Subscriber *s, std::string_view topic) { | 187 | | /* Notify user that they are doing something wrong here */ | 188 | 649 | checkIteratingSubscriber(s); | 189 | | | 190 | | /* Lookup topic */ | 191 | 649 | Topic *topicPtr = lookupTopic(topic); | 192 | 649 | if (!topicPtr) { | 193 | | /* If the topic doesn't exist we are assumed to still be subscribers of something */ | 194 | 649 | return {false, false, -1}; | 195 | 649 | } | 196 | | | 197 | | /* Erase from our list first */ | 198 | 0 | if (s->topics.erase(topicPtr) == 0) { | 199 | 0 | return {false, false, -1}; | 200 | 0 | } | 201 | | | 202 | | /* Remove us from topic */ | 203 | 0 | topicPtr->erase(s); | 204 | |
| 205 | 0 | int newCount = (int) topicPtr->size(); | 206 | | | 207 | | /* If there is no subscriber to this topic, remove it */ | 208 | 0 | if (!topicPtr->size()) { | 209 | | /* Unique_ptr deletes the topic */ | 210 | 0 | topics.erase(topic); | 211 | 0 | } | 212 | | | 213 | | /* If we don't hold any topics we are to be freed altogether */ | 214 | 0 | return {true, s->topics.size() == 0, newCount}; | 215 | 0 | } |
|
216 | | |
217 | | /* Factory function for creating a Subscriber */ |
218 | 2.33M | Subscriber *createSubscriber() { |
219 | 2.33M | return new Subscriber(); |
220 | 2.33M | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::createSubscriber() Line | Count | Source | 218 | 2.09M | Subscriber *createSubscriber() { | 219 | 2.09M | return new Subscriber(); | 220 | 2.09M | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::createSubscriber() Line | Count | Source | 218 | 237k | Subscriber *createSubscriber() { | 219 | 237k | return new Subscriber(); | 220 | 237k | } |
|
221 | | |
222 | | /* This is used to end a Subscriber, before freeing it */ |
223 | 2.40M | void freeSubscriber(Subscriber *s) { |
224 | | |
225 | | /* I guess we call this one even if we are not subscribers */ |
226 | 2.40M | if (!s) { |
227 | 68.1k | return; |
228 | 68.1k | } |
229 | | |
230 | | /* For all topics, unsubscribe */ |
231 | 8.76M | for (Topic *topicPtr : s->topics) { |
232 | | /* If we are the last subscriber, simply remove the whole topic */ |
233 | 8.76M | if (topicPtr->size() == 1) { |
234 | 6.73M | topics.erase(topicPtr->name); |
235 | 6.73M | } else { |
236 | | /* Otherwise just remove us */ |
237 | 2.03M | topicPtr->erase(s); |
238 | 2.03M | } |
239 | 8.76M | } |
240 | | |
241 | | /* We also need to unlink us */ |
242 | 2.33M | if (s->needsDrainage()) { |
243 | 157k | unlinkDrainableSubscriber(s); |
244 | 157k | } |
245 | | |
246 | 2.33M | delete s; |
247 | 2.33M | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::freeSubscriber(uWS::Subscriber*) Line | Count | Source | 223 | 2.09M | void freeSubscriber(Subscriber *s) { | 224 | | | 225 | | /* I guess we call this one even if we are not subscribers */ | 226 | 2.09M | if (!s) { | 227 | 0 | return; | 228 | 0 | } | 229 | | | 230 | | /* For all topics, unsubscribe */ | 231 | 2.12M | for (Topic *topicPtr : s->topics) { | 232 | | /* If we are the last subscriber, simply remove the whole topic */ | 233 | 2.12M | if (topicPtr->size() == 1) { | 234 | 227k | topics.erase(topicPtr->name); | 235 | 1.89M | } else { | 236 | | /* Otherwise just remove us */ | 237 | 1.89M | topicPtr->erase(s); | 238 | 1.89M | } | 239 | 2.12M | } | 240 | | | 241 | | /* We also need to unlink us */ | 242 | 2.09M | if (s->needsDrainage()) { | 243 | 73.8k | unlinkDrainableSubscriber(s); | 244 | 73.8k | } | 245 | | | 246 | 2.09M | delete s; | 247 | 2.09M | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::freeSubscriber(uWS::Subscriber*) Line | Count | Source | 223 | 306k | void freeSubscriber(Subscriber *s) { | 224 | | | 225 | | /* I guess we call this one even if we are not subscribers */ | 226 | 306k | if (!s) { | 227 | 68.1k | return; | 228 | 68.1k | } | 229 | | | 230 | | /* For all topics, unsubscribe */ | 231 | 6.64M | for (Topic *topicPtr : s->topics) { | 232 | | /* If we are the last subscriber, simply remove the whole topic */ | 233 | 6.64M | if (topicPtr->size() == 1) { | 234 | 6.51M | topics.erase(topicPtr->name); | 235 | 6.51M | } else { | 236 | | /* Otherwise just remove us */ | 237 | 135k | topicPtr->erase(s); | 238 | 135k | } | 239 | 6.64M | } | 240 | | | 241 | | /* We also need to unlink us */ | 242 | 237k | if (s->needsDrainage()) { | 243 | 83.9k | unlinkDrainableSubscriber(s); | 244 | 83.9k | } | 245 | | | 246 | 237k | delete s; | 247 | 237k | } |
|
248 | | |
249 | | /* Mainly used by WebSocket::send to drain one socket before sending */ |
250 | 515k | void drain(Subscriber *s) { |
251 | | /* The list is undefined and cannot be touched unless needsDrainage(). */ |
252 | 515k | if (s->needsDrainage()) { |
253 | | /* This function differs from drainImpl by properly unlinking |
254 | | * the subscriber from drainableSubscribers. drainImpl does not. */ |
255 | 128k | unlinkDrainableSubscriber(s); |
256 | | |
257 | | /* This one always resets needsDrainage before it calls any cb's. |
258 | | * Otherwise we would stackoverflow when sending after publish but before drain. */ |
259 | 128k | drainImpl(s); |
260 | | |
261 | | /* If we drained last subscriber, also clear outgoingMessages */ |
262 | 128k | if (!drainableSubscribers) { |
263 | 8.36k | outgoingMessages.clear(); |
264 | 8.36k | } |
265 | 128k | } |
266 | 515k | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::drain(uWS::Subscriber*) Line | Count | Source | 250 | 110k | void drain(Subscriber *s) { | 251 | | /* The list is undefined and cannot be touched unless needsDrainage(). */ | 252 | 110k | if (s->needsDrainage()) { | 253 | | /* This function differs from drainImpl by properly unlinking | 254 | | * the subscriber from drainableSubscribers. drainImpl does not. */ | 255 | 109k | unlinkDrainableSubscriber(s); | 256 | | | 257 | | /* This one always resets needsDrainage before it calls any cb's. | 258 | | * Otherwise we would stackoverflow when sending after publish but before drain. */ | 259 | 109k | drainImpl(s); | 260 | | | 261 | | /* If we drained last subscriber, also clear outgoingMessages */ | 262 | 109k | if (!drainableSubscribers) { | 263 | 554 | outgoingMessages.clear(); | 264 | 554 | } | 265 | 109k | } | 266 | 110k | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::drain(uWS::Subscriber*) Line | Count | Source | 250 | 405k | void drain(Subscriber *s) { | 251 | | /* The list is undefined and cannot be touched unless needsDrainage(). */ | 252 | 405k | if (s->needsDrainage()) { | 253 | | /* This function differs from drainImpl by properly unlinking | 254 | | * the subscriber from drainableSubscribers. drainImpl does not. */ | 255 | 18.8k | unlinkDrainableSubscriber(s); | 256 | | | 257 | | /* This one always resets needsDrainage before it calls any cb's. | 258 | | * Otherwise we would stackoverflow when sending after publish but before drain. */ | 259 | 18.8k | drainImpl(s); | 260 | | | 261 | | /* If we drained last subscriber, also clear outgoingMessages */ | 262 | 18.8k | if (!drainableSubscribers) { | 263 | 7.80k | outgoingMessages.clear(); | 264 | 7.80k | } | 265 | 18.8k | } | 266 | 405k | } |
|
267 | | |
268 | | /* Called everytime we call send, to drain published messages so to sync outgoing messages */ |
269 | 21.5M | void drain() { |
270 | 21.5M | if (drainableSubscribers) { |
271 | | /* Drain one socket a time */ |
272 | 99.6k | for (Subscriber *s = drainableSubscribers; s; s = s->next) { |
273 | | /* Instead of unlinking every single subscriber, we just leave the list undefined |
274 | | * and reset drainableSubscribers ptr below. */ |
275 | 86.3k | drainImpl(s); |
276 | 86.3k | } |
277 | | /* Drain always clears drainableSubscribers and outgoingMessages */ |
278 | 13.2k | drainableSubscribers = nullptr; |
279 | 13.2k | outgoingMessages.clear(); |
280 | 13.2k | } |
281 | 21.5M | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::drain() Line | Count | Source | 269 | 156k | void drain() { | 270 | 156k | if (drainableSubscribers) { | 271 | | /* Drain one socket a time */ | 272 | 80.9k | for (Subscriber *s = drainableSubscribers; s; s = s->next) { | 273 | | /* Instead of unlinking every single subscriber, we just leave the list undefined | 274 | | * and reset drainableSubscribers ptr below. */ | 275 | 75.3k | drainImpl(s); | 276 | 75.3k | } | 277 | | /* Drain always clears drainableSubscribers and outgoingMessages */ | 278 | 5.64k | drainableSubscribers = nullptr; | 279 | 5.64k | outgoingMessages.clear(); | 280 | 5.64k | } | 281 | 156k | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::drain() Line | Count | Source | 269 | 21.4M | void drain() { | 270 | 21.4M | if (drainableSubscribers) { | 271 | | /* Drain one socket a time */ | 272 | 18.6k | for (Subscriber *s = drainableSubscribers; s; s = s->next) { | 273 | | /* Instead of unlinking every single subscriber, we just leave the list undefined | 274 | | * and reset drainableSubscribers ptr below. */ | 275 | 11.0k | drainImpl(s); | 276 | 11.0k | } | 277 | | /* Drain always clears drainableSubscribers and outgoingMessages */ | 278 | 7.63k | drainableSubscribers = nullptr; | 279 | 7.63k | outgoingMessages.clear(); | 280 | 7.63k | } | 281 | 21.4M | } |
|
282 | | |
283 | | /* Big messages bypass all buffering and land directly in backpressure */ |
284 | | template <typename F> |
285 | 0 | bool publishBig(Subscriber *sender, std::string_view topic, B &&bigMessage, F cb) { |
286 | | /* Do we even have this topic? */ |
287 | 0 | auto it = topics.find(topic); |
288 | 0 | if (it == topics.end()) { |
289 | 0 | return false; |
290 | 0 | } |
291 | | |
292 | | /* For all subscribers in topic */ |
293 | 0 | for (Subscriber *s : *it->second) { |
294 | | |
295 | | /* If we are sender then ignore us */ |
296 | 0 | if (sender != s) { |
297 | 0 | cb(s, bigMessage); |
298 | 0 | } |
299 | 0 | } |
300 | |
|
301 | 0 | return true; |
302 | 0 | } Unexecuted instantiation: bool uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::publishBig<uWS::TemplatedApp<true>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1}>(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::TopicTreeBigMessage&&, uWS::TemplatedApp<true>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1})Unexecuted instantiation: EpollEchoServer.cpp:bool uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::publishBig<uWS::WebSocket<false, true, test()::PerSocketData>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1}>(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::TopicTreeBigMessage&&, uWS::WebSocket<false, true, test()::PerSocketData>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1})Unexecuted instantiation: bool uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::publishBig<uWS::TemplatedApp<false>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1}>(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::TopicTreeBigMessage&&, uWS::TemplatedApp<false>::publish(std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::OpCode, bool)::{lambda(uWS::Subscriber*, uWS::TopicTreeBigMessage&)#1})Unexecuted instantiation: bool uWS::TopicTree<uWS::TopicTreeMessage, uWS::PreparedMessage*>::publishBig<uWS::TemplatedApp<false>::publishPrepared(std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::PreparedMessage&)::{lambda(uWS::Subscriber*, uWS::PreparedMessage*)#1}>(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::PreparedMessage*&&, uWS::TemplatedApp<false>::publishPrepared(std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::PreparedMessage&)::{lambda(uWS::Subscriber*, uWS::PreparedMessage*)#1}) |
303 | | |
304 | | /* Linear in number of affected subscribers */ |
305 | 1.36M | bool publish(Subscriber *sender, std::string_view topic, T &&message) { |
306 | | /* Do we even have this topic? */ |
307 | 1.36M | auto it = topics.find(topic); |
308 | 1.36M | if (it == topics.end()) { |
309 | 43.1k | return false; |
310 | 43.1k | } |
311 | | |
312 | | /* If we have more than 65k messages we need to drain every socket. */ |
313 | 1.32M | if (outgoingMessages.size() == UINT16_MAX) { |
314 | | /* If there is a socket that is currently corked, this will be ugly as all sockets will drain |
315 | | * to their own backpressure */ |
316 | 3 | drain(); |
317 | 3 | } |
318 | | |
319 | | /* If nobody references this message, don't buffer it */ |
320 | 1.32M | bool referencedMessage = false; |
321 | | |
322 | | /* For all subscribers in topic */ |
323 | 4.88M | for (Subscriber *s : *it->second) { |
324 | | |
325 | | /* If we are sender then ignore us */ |
326 | 4.88M | if (sender != s) { |
327 | | |
328 | | /* At least one subscriber wants this message */ |
329 | 4.75M | referencedMessage = true; |
330 | | |
331 | | /* If we already have too many outgoing messages on this subscriber, drain it now */ |
332 | 4.75M | if (s->numMessageIndices == 32) { |
333 | | /* This one does not need to check needsDrainage here but still does. */ |
334 | 117k | drain(s); |
335 | 117k | } |
336 | | |
337 | | /* Finally we can continue */ |
338 | 4.75M | s->messageIndices[s->numMessageIndices++] = (uint16_t)outgoingMessages.size(); |
339 | | /* First message adds subscriber to list of drainable subscribers */ |
340 | 4.75M | if (s->numMessageIndices == 1) { |
341 | | /* Insert us in the head of drainable subscribers */ |
342 | 372k | s->next = drainableSubscribers; |
343 | 372k | s->prev = nullptr; |
344 | 372k | if (s->next) { |
345 | 291k | s->next->prev = s; |
346 | 291k | } |
347 | 372k | drainableSubscribers = s; |
348 | 372k | } |
349 | 4.75M | } |
350 | 4.88M | } |
351 | | |
352 | | /* Push this message and return with success */ |
353 | 1.32M | if (referencedMessage) { |
354 | 1.32M | outgoingMessages.emplace_back(message); |
355 | 1.32M | } |
356 | | |
357 | | /* Success if someone wants it */ |
358 | 1.32M | return referencedMessage; |
359 | 1.36M | } uWS::TopicTree<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::basic_string_view<char, std::__1::char_traits<char> > >::publish(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&&) Line | Count | Source | 305 | 1.09M | bool publish(Subscriber *sender, std::string_view topic, T &&message) { | 306 | | /* Do we even have this topic? */ | 307 | 1.09M | auto it = topics.find(topic); | 308 | 1.09M | if (it == topics.end()) { | 309 | 24.8k | return false; | 310 | 24.8k | } | 311 | | | 312 | | /* If we have more than 65k messages we need to drain every socket. */ | 313 | 1.07M | if (outgoingMessages.size() == UINT16_MAX) { | 314 | | /* If there is a socket that is currently corked, this will be ugly as all sockets will drain | 315 | | * to their own backpressure */ | 316 | 3 | drain(); | 317 | 3 | } | 318 | | | 319 | | /* If nobody references this message, don't buffer it */ | 320 | 1.07M | bool referencedMessage = false; | 321 | | | 322 | | /* For all subscribers in topic */ | 323 | 4.11M | for (Subscriber *s : *it->second) { | 324 | | | 325 | | /* If we are sender then ignore us */ | 326 | 4.11M | if (sender != s) { | 327 | | | 328 | | /* At least one subscriber wants this message */ | 329 | 4.11M | referencedMessage = true; | 330 | | | 331 | | /* If we already have too many outgoing messages on this subscriber, drain it now */ | 332 | 4.11M | if (s->numMessageIndices == 32) { | 333 | | /* This one does not need to check needsDrainage here but still does. */ | 334 | 108k | drain(s); | 335 | 108k | } | 336 | | | 337 | | /* Finally we can continue */ | 338 | 4.11M | s->messageIndices[s->numMessageIndices++] = (uint16_t)outgoingMessages.size(); | 339 | | /* First message adds subscriber to list of drainable subscribers */ | 340 | 4.11M | if (s->numMessageIndices == 1) { | 341 | | /* Insert us in the head of drainable subscribers */ | 342 | 258k | s->next = drainableSubscribers; | 343 | 258k | s->prev = nullptr; | 344 | 258k | if (s->next) { | 345 | 225k | s->next->prev = s; | 346 | 225k | } | 347 | 258k | drainableSubscribers = s; | 348 | 258k | } | 349 | 4.11M | } | 350 | 4.11M | } | 351 | | | 352 | | /* Push this message and return with success */ | 353 | 1.07M | if (referencedMessage) { | 354 | 1.07M | outgoingMessages.emplace_back(message); | 355 | 1.07M | } | 356 | | | 357 | | /* Success if someone wants it */ | 358 | 1.07M | return referencedMessage; | 359 | 1.09M | } |
uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::publish(uWS::Subscriber*, std::__1::basic_string_view<char, std::__1::char_traits<char> >, uWS::TopicTreeMessage&&) Line | Count | Source | 305 | 271k | bool publish(Subscriber *sender, std::string_view topic, T &&message) { | 306 | | /* Do we even have this topic? */ | 307 | 271k | auto it = topics.find(topic); | 308 | 271k | if (it == topics.end()) { | 309 | 18.2k | return false; | 310 | 18.2k | } | 311 | | | 312 | | /* If we have more than 65k messages we need to drain every socket. */ | 313 | 252k | if (outgoingMessages.size() == UINT16_MAX) { | 314 | | /* If there is a socket that is currently corked, this will be ugly as all sockets will drain | 315 | | * to their own backpressure */ | 316 | 0 | drain(); | 317 | 0 | } | 318 | | | 319 | | /* If nobody references this message, don't buffer it */ | 320 | 252k | bool referencedMessage = false; | 321 | | | 322 | | /* For all subscribers in topic */ | 323 | 770k | for (Subscriber *s : *it->second) { | 324 | | | 325 | | /* If we are sender then ignore us */ | 326 | 770k | if (sender != s) { | 327 | | | 328 | | /* At least one subscriber wants this message */ | 329 | 640k | referencedMessage = true; | 330 | | | 331 | | /* If we already have too many outgoing messages on this subscriber, drain it now */ | 332 | 640k | if (s->numMessageIndices == 32) { | 333 | | /* This one does not need to check needsDrainage here but still does. */ | 334 | 9.58k | drain(s); | 335 | 9.58k | } | 336 | | | 337 | | /* Finally we can continue */ | 338 | 640k | s->messageIndices[s->numMessageIndices++] = (uint16_t)outgoingMessages.size(); | 339 | | /* First message adds subscriber to list of drainable subscribers */ | 340 | 640k | if (s->numMessageIndices == 1) { | 341 | | /* Insert us in the head of drainable subscribers */ | 342 | 113k | s->next = drainableSubscribers; | 343 | 113k | s->prev = nullptr; | 344 | 113k | if (s->next) { | 345 | 65.5k | s->next->prev = s; | 346 | 65.5k | } | 347 | 113k | drainableSubscribers = s; | 348 | 113k | } | 349 | 640k | } | 350 | 770k | } | 351 | | | 352 | | /* Push this message and return with success */ | 353 | 252k | if (referencedMessage) { | 354 | 251k | outgoingMessages.emplace_back(message); | 355 | 251k | } | 356 | | | 357 | | /* Success if someone wants it */ | 358 | 252k | return referencedMessage; | 359 | 271k | } |
|
360 | | }; |
361 | | |
362 | | } |
363 | | |
364 | | #endif |